GCC Code Coverage Report


Directory: ./
File: storage/innobase/handler/handler0alter.cc
Date: 2022-12-13 11:44:05
Exec Total Coverage
Lines: 4196 4777 87.8%
Branches: 3875 6423 60.3%

Line Branch Exec Source
1 /*****************************************************************************
2
3 Copyright (c) 2005, 2022, Oracle and/or its affiliates.
4
5 This program is free software; you can redistribute it and/or modify it under
6 the terms of the GNU General Public License, version 2.0, as published by the
7 Free Software Foundation.
8
9 This program is also distributed with certain software (including but not
10 limited to OpenSSL) that is licensed under separate terms, as designated in a
11 particular file or component or in included license documentation. The authors
12 of MySQL hereby grant you an additional permission to link the program and
13 your derivative works with the separately licensed software that they have
14 included with MySQL.
15
16 This program is distributed in the hope that it will be useful, but WITHOUT
17 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
18 FOR A PARTICULAR PURPOSE. See the GNU General Public License, version 2.0,
19 for more details.
20
21 You should have received a copy of the GNU General Public License along with
22 this program; if not, write to the Free Software Foundation, Inc.,
23 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
24
25 *****************************************************************************/
26
27 /** @file handler/handler0alter.cc
28 Smart ALTER TABLE
29 *******************************************************/
30
31 /* Include necessary SQL headers */
32 #include <assert.h>
33 #include <current_thd.h>
34 #include <debug_sync.h>
35 #include <key_spec.h>
36 #include <log.h>
37 #include <my_bit.h>
38 #include <mysql/plugin.h>
39 #include <sql_class.h>
40 #include <sql_lex.h>
41 #include <sql_table.h>
42 #include <sql_thd_internal_api.h>
43 #include <sys/types.h>
44 #include "ha_prototypes.h"
45
46 #include "dd/cache/dictionary_client.h"
47 #include "dd/dd.h"
48 #include "dd/dictionary.h"
49 #include "dd/impl/properties_impl.h"
50 #include "dd/impl/types/column_impl.h"
51 #include "dd/properties.h"
52 #include "dd/types/column.h"
53 #include "dd/types/column_type_element.h"
54 #include "dd/types/index.h"
55 #include "dd/types/index_element.h"
56 #include "dd/types/partition.h"
57 #include "dd/types/partition_index.h"
58 #include "dd/types/table.h"
59 #include "dd/types/tablespace_file.h"
60 #include "dd_table_share.h"
61
62 #include "btr0sea.h"
63 #include "dict0crea.h"
64 #include "dict0dd.h"
65 #include "dict0dict.h"
66 #include "dict0inst.h" //Instant DDL
67 #include "dict0priv.h"
68 #include "dict0stats.h"
69 #include "dict0stats_bg.h"
70 #include "fsp0sysspace.h"
71 #include "fts0plugin.h"
72 #include "fts0priv.h"
73 #include "ha_innodb.h"
74 #include "ha_innopart.h"
75 #include "ha_prototypes.h"
76 #include "handler0alter.h"
77 #include "lex_string.h"
78 #include "log0buf.h"
79 #include "log0chkp.h"
80
81 #include "my_dbug.h"
82 #include "my_io.h"
83
84 #include "clone0api.h"
85 #include "ddl0ddl.h"
86 #include "dict0dd.h"
87 #include "fts0plugin.h"
88 #include "fts0priv.h"
89 #include "handler0alter.h"
90 #include "lock0lock.h"
91 #include "pars0pars.h"
92 #include "partition_info.h"
93 #include "rem0types.h"
94 #include "row0ins.h"
95 #include "row0log.h"
96 #include "row0sel.h"
97 #include "sql/create_field.h"
98 #include "srv0mon.h"
99 #include "trx0roll.h"
100 #include "trx0trx.h"
101 #include "ut0new.h"
102 #include "ut0stage.h"
103
104 /* For supporting Native InnoDB Partitioning. */
105 #include "ha_innopart.h"
106 #include "partition_info.h"
107
108 extern char server_uuid[UUID_LENGTH + 1];
109
110 /** Function to convert the Instant_Type to a comparable int */
111 592347 inline uint16_t instant_type_to_int(Instant_Type type) {
112 592347 return (static_cast<typename std::underlying_type<Log_Type>::type>(type));
113 }
114
115 /** Operations for creating secondary indexes (no rebuild needed) */
116 static const Alter_inplace_info::HA_ALTER_FLAGS INNOBASE_ONLINE_CREATE =
117 Alter_inplace_info::ADD_INDEX | Alter_inplace_info::ADD_UNIQUE_INDEX |
118 Alter_inplace_info::ADD_SPATIAL_INDEX;
119
120 /** Operations for rebuilding a table in place */
121 static const Alter_inplace_info::HA_ALTER_FLAGS INNOBASE_ALTER_REBUILD =
122 Alter_inplace_info::ADD_PK_INDEX | Alter_inplace_info::DROP_PK_INDEX |
123 Alter_inplace_info::CHANGE_CREATE_OPTION
124 /* CHANGE_CREATE_OPTION needs to check innobase_need_rebuild() */
125 | Alter_inplace_info::ALTER_COLUMN_NULLABLE |
126 Alter_inplace_info::ALTER_COLUMN_NOT_NULLABLE |
127 Alter_inplace_info::ALTER_STORED_COLUMN_ORDER |
128 Alter_inplace_info::DROP_STORED_COLUMN |
129 Alter_inplace_info::ADD_STORED_BASE_COLUMN
130 /* ADD_STORED_BASE_COLUMN needs to check innobase_need_rebuild() */
131 | Alter_inplace_info::RECREATE_TABLE;
132
133 /** Operations that require changes to data */
134 static const Alter_inplace_info::HA_ALTER_FLAGS INNOBASE_ALTER_DATA =
135 INNOBASE_ONLINE_CREATE | INNOBASE_ALTER_REBUILD;
136
137 /** Operations for altering a table that InnoDB does not care about */
138 static const Alter_inplace_info::HA_ALTER_FLAGS INNOBASE_INPLACE_IGNORE =
139 Alter_inplace_info::ALTER_COLUMN_DEFAULT |
140 Alter_inplace_info::ALTER_COLUMN_COLUMN_FORMAT |
141 Alter_inplace_info::ALTER_COLUMN_STORAGE_TYPE |
142 Alter_inplace_info::ALTER_RENAME | Alter_inplace_info::CHANGE_INDEX_OPTION |
143 Alter_inplace_info::ADD_CHECK_CONSTRAINT |
144 Alter_inplace_info::DROP_CHECK_CONSTRAINT |
145 Alter_inplace_info::SUSPEND_CHECK_CONSTRAINT |
146 Alter_inplace_info::ALTER_COLUMN_VISIBILITY;
147
148 /** Operation allowed with ALGORITHM=INSTANT */
149 static const Alter_inplace_info::HA_ALTER_FLAGS INNOBASE_INSTANT_ALLOWED =
150 Alter_inplace_info::ALTER_COLUMN_NAME |
151 Alter_inplace_info::ADD_VIRTUAL_COLUMN |
152 Alter_inplace_info::DROP_VIRTUAL_COLUMN |
153 Alter_inplace_info::ALTER_VIRTUAL_COLUMN_ORDER |
154 Alter_inplace_info::ADD_STORED_BASE_COLUMN |
155 Alter_inplace_info::ALTER_STORED_COLUMN_ORDER |
156 Alter_inplace_info::DROP_STORED_COLUMN;
157
158 /** Operations on foreign key definitions (changing the schema only) */
159 static const Alter_inplace_info::HA_ALTER_FLAGS INNOBASE_FOREIGN_OPERATIONS =
160 Alter_inplace_info::DROP_FOREIGN_KEY | Alter_inplace_info::ADD_FOREIGN_KEY;
161
162 /** Operations that InnoDB cares about and can perform without rebuild */
163 static const Alter_inplace_info::HA_ALTER_FLAGS INNOBASE_ALTER_NOREBUILD =
164 INNOBASE_ONLINE_CREATE | INNOBASE_FOREIGN_OPERATIONS |
165 Alter_inplace_info::DROP_INDEX | Alter_inplace_info::DROP_UNIQUE_INDEX |
166 Alter_inplace_info::RENAME_INDEX | Alter_inplace_info::ALTER_COLUMN_NAME |
167 Alter_inplace_info::ALTER_COLUMN_EQUAL_PACK_LENGTH |
168 Alter_inplace_info::ALTER_INDEX_COMMENT |
169 Alter_inplace_info::ADD_VIRTUAL_COLUMN |
170 Alter_inplace_info::DROP_VIRTUAL_COLUMN |
171 Alter_inplace_info::ALTER_VIRTUAL_COLUMN_ORDER |
172 Alter_inplace_info::ALTER_COLUMN_INDEX_LENGTH;
173
174 struct ha_innobase_inplace_ctx : public inplace_alter_handler_ctx {
175 /** Dummy query graph */
176 que_thr_t *thr;
177 /** The prebuilt struct of the creating instance */
178 row_prebuilt_t *prebuilt;
179 /** InnoDB indexes being created */
180 dict_index_t **add_index;
181 /** MySQL key numbers for the InnoDB indexes that are being created */
182 const ulint *add_key_numbers;
183 /** number of InnoDB indexes being created */
184 ulint num_to_add_index;
185 /** InnoDB indexes being dropped */
186 dict_index_t **drop_index;
187 /** number of InnoDB indexes being dropped */
188 const ulint num_to_drop_index;
189 /** InnoDB indexes being renamed */
190 dict_index_t **rename;
191 /** number of InnoDB indexes being renamed */
192 const ulint num_to_rename;
193 /** InnoDB foreign key constraints being dropped */
194 dict_foreign_t **drop_fk;
195 /** number of InnoDB foreign key constraints being dropped */
196 const ulint num_to_drop_fk;
197 /** InnoDB foreign key constraints being added */
198 dict_foreign_t **add_fk;
199 /** number of InnoDB foreign key constraints being dropped */
200 const ulint num_to_add_fk;
201 /** whether to create the indexes online */
202 bool online;
203 /** memory heap */
204 mem_heap_t *heap;
205 /** dictionary transaction */
206 trx_t *trx;
207 /** original table (if rebuilt, differs from indexed_table) */
208 dict_table_t *old_table;
209 /** table where the indexes are being created or dropped */
210 dict_table_t *new_table;
211 /** mapping of old column numbers to new ones, or NULL */
212 const ulint *col_map;
213 /** new column names, or NULL if nothing was renamed */
214 const char **col_names;
215 /** added AUTO_INCREMENT column position, or ULINT_UNDEFINED */
216 const ulint add_autoinc;
217 /** default values of ADD COLUMN, or NULL */
218 const dtuple_t *add_cols;
219 /** autoinc sequence to use */
220 ddl::Sequence sequence;
221 /** maximum auto-increment value */
222 ulonglong max_autoinc;
223 /** temporary table name to use for old table when renaming tables */
224 const char *tmp_name;
225 /** whether the order of the clustered index is unchanged */
226 bool skip_pk_sort;
227 /** virtual columns to be added */
228 dict_v_col_t *add_vcol;
229 const char **add_vcol_name;
230 /** virtual columns to be dropped */
231 dict_v_col_t *drop_vcol;
232 const char **drop_vcol_name;
233 /** ALTER TABLE stage progress recorder */
234 Alter_stage *m_stage;
235 /** FTS AUX Tables to drop */
236 aux_name_vec_t *fts_drop_aux_vec;
237
238 59987 ha_innobase_inplace_ctx(row_prebuilt_t *prebuilt_arg, dict_index_t **drop_arg,
239 ulint num_to_drop_arg, dict_index_t **rename_arg,
240 ulint num_to_rename_arg, dict_foreign_t **drop_fk_arg,
241 ulint num_to_drop_fk_arg, dict_foreign_t **add_fk_arg,
242 ulint num_to_add_fk_arg, bool online_arg,
243 mem_heap_t *heap_arg, dict_table_t *new_table_arg,
244 const char **col_names_arg, ulint add_autoinc_arg,
245 ulonglong autoinc_col_min_value_arg,
246 ulonglong autoinc_col_max_value_arg)
247 59987 : inplace_alter_handler_ctx(),
248 59987 prebuilt(prebuilt_arg),
249 59987 add_index(nullptr),
250 59987 add_key_numbers(nullptr),
251 59987 num_to_add_index(0),
252 59987 drop_index(drop_arg),
253 59987 num_to_drop_index(num_to_drop_arg),
254 59987 rename(rename_arg),
255 59987 num_to_rename(num_to_rename_arg),
256 59987 drop_fk(drop_fk_arg),
257 59987 num_to_drop_fk(num_to_drop_fk_arg),
258 59987 add_fk(add_fk_arg),
259 59987 num_to_add_fk(num_to_add_fk_arg),
260 59987 online(online_arg),
261 59987 heap(heap_arg),
262 59987 trx(nullptr),
263 59987 old_table(prebuilt_arg->table),
264 59987 new_table(new_table_arg),
265 59987 col_map(nullptr),
266 59987 col_names(col_names_arg),
267 59987 add_autoinc(add_autoinc_arg),
268 59987 add_cols(nullptr),
269 59987 sequence(prebuilt->trx->mysql_thd, autoinc_col_min_value_arg,
270 autoinc_col_max_value_arg),
271 59987 max_autoinc(0),
272 59987 tmp_name(nullptr),
273 59987 skip_pk_sort(false),
274 59987 add_vcol(nullptr),
275 59987 add_vcol_name(nullptr),
276 59987 drop_vcol(nullptr),
277 59987 drop_vcol_name(nullptr),
278 59987 m_stage(nullptr),
279 59987 fts_drop_aux_vec(nullptr) {
280 #ifdef UNIV_DEBUG
281
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 59987 times.
59987 for (ulint i = 0; i < num_to_add_index; i++) {
282 ut_ad(!add_index[i]->to_be_dropped);
283 }
284
2/2
✓ Branch 0 taken 3802 times.
✓ Branch 1 taken 59987 times.
63789 for (ulint i = 0; i < num_to_drop_index; i++) {
285
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3802 times.
3802 ut_ad(drop_index[i]->to_be_dropped);
286 }
287 #endif /* UNIV_DEBUG */
288
289
1/2
✓ Branch 0 taken 59987 times.
✗ Branch 1 not taken.
59987 thr = pars_complete_graph_for_exec(nullptr, prebuilt->trx, heap, prebuilt);
290 59987 }
291
292 119216 ~ha_innobase_inplace_ctx() override {
293
2/2
✓ Branch 0 taken 92 times.
✓ Branch 1 taken 59516 times.
119216 if (fts_drop_aux_vec != nullptr) {
294 184 fts_free_aux_names(fts_drop_aux_vec);
295
1/2
✓ Branch 0 taken 92 times.
✗ Branch 1 not taken.
184 delete fts_drop_aux_vec;
296 }
297 119216 ut::delete_(m_stage);
298 119216 mem_heap_free(heap);
299 }
300
301 /** Determine if the table will be rebuilt.
302 @return whether the table will be rebuilt */
303 1096295 bool need_rebuild() const { return (old_table != new_table); }
304
305 /** Set shared data between the passed in handler context
306 and current context.
307 @param[in] ctx handler context */
308 5762 void set_shared_data(const inplace_alter_handler_ctx *ctx) override {
309
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5762 times.
5762 ut_ad(ctx != nullptr);
310
2/2
✓ Branch 0 taken 5759 times.
✓ Branch 1 taken 3 times.
5762 if (add_autoinc == ULINT_UNDEFINED) {
311 5759 return;
312 }
313 3 const ha_innobase_inplace_ctx *ha_ctx =
314 static_cast<const ha_innobase_inplace_ctx *>(ctx);
315
316 /* In InnoDB table, if it's adding AUTOINC column,
317 the sequence value should be shared among contexts */
318
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 ut_ad(ha_ctx->add_autoinc != ULINT_UNDEFINED);
319 3 sequence = ha_ctx->sequence;
320 }
321
322 private:
323 // Disable copying
324 ha_innobase_inplace_ctx(const ha_innobase_inplace_ctx &);
325 ha_innobase_inplace_ctx &operator=(const ha_innobase_inplace_ctx &);
326 };
327
328 /** Structure to remember table information for updating DD */
329 struct alter_table_old_info_t {
330 /** Constructor */
331 71096 alter_table_old_info_t() : m_discarded(), m_fts_doc_id(), m_rebuild() {}
332
333 /** If old table is discarded one */
334 bool m_discarded;
335
336 /** If old table has FTS DOC ID */
337 bool m_fts_doc_id;
338
339 /** If this ATLER TABLE requires rebuild */
340 bool m_rebuild;
341
342 /** Update the old table information
343 @param[in] old_table Old InnoDB table object
344 @param[in] rebuild True if rebuild is necessary */
345 59313 void update(const dict_table_t *old_table, bool rebuild) {
346 59313 m_discarded = dict_table_is_discarded(old_table);
347 59313 m_fts_doc_id = DICT_TF2_FLAG_IS_SET(old_table, DICT_TF2_FTS_HAS_DOC_ID);
348 59313 m_rebuild = rebuild;
349 59313 }
350 };
351
352 /* Report an InnoDB error to the client by invoking my_error(). */
353 118 static UNIV_COLD void my_error_innodb(
354 dberr_t error, /*!< in: InnoDB error code */
355 const char *table, /*!< in: table name */
356 uint32_t flags) /*!< in: table flags */
357 {
358
10/20
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 10 times.
✓ Branch 5 taken 16 times.
✓ Branch 6 taken 4 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 1 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 2 times.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✓ Branch 14 taken 16 times.
✓ Branch 15 taken 13 times.
✗ Branch 16 not taken.
✓ Branch 17 taken 3 times.
✗ Branch 18 not taken.
✓ Branch 19 taken 52 times.
118 switch (error) {
359 case DB_MISSING_HISTORY:
360 my_error(ER_TABLE_DEF_CHANGED, MYF(0));
361 break;
362 case DB_RECORD_NOT_FOUND:
363 my_error(ER_KEY_NOT_FOUND, MYF(0), table);
364 break;
365 1 case DB_DEADLOCK:
366 1 my_error(ER_LOCK_DEADLOCK, MYF(0));
367 1 break;
368 case DB_LOCK_WAIT_TIMEOUT:
369 my_error(ER_LOCK_WAIT_TIMEOUT, MYF(0));
370 break;
371 10 case DB_INTERRUPTED:
372 10 my_error(ER_QUERY_INTERRUPTED, MYF(0));
373 10 break;
374 16 case DB_OUT_OF_MEMORY:
375 16 my_error(ER_OUT_OF_RESOURCES, MYF(0));
376 16 break;
377 4 case DB_OUT_OF_FILE_SPACE:
378 4 my_error(ER_RECORD_FILE_FULL, MYF(0), table);
379 4 break;
380 case DB_OUT_OF_DISK_SPACE:
381 my_error(ER_DISK_FULL_NOWAIT, MYF(0), table);
382 break;
383 1 case DB_TEMP_FILE_WRITE_FAIL:
384 1 my_error(ER_TEMP_FILE_WRITE_FAILURE, MYF(0));
385 1 break;
386 case DB_TOO_BIG_INDEX_COL:
387 my_error(ER_INDEX_COLUMN_TOO_LONG, MYF(0),
388 DICT_MAX_FIELD_LEN_BY_FORMAT_FLAG(flags));
389 break;
390 2 case DB_TOO_MANY_CONCURRENT_TRXS:
391 2 my_error(ER_TOO_MANY_CONCURRENT_TRXS, MYF(0));
392 2 break;
393 case DB_LOCK_TABLE_FULL:
394 my_error(ER_LOCK_TABLE_FULL, MYF(0));
395 break;
396 case DB_UNDO_RECORD_TOO_BIG:
397 my_error(ER_UNDO_RECORD_TOO_BIG, MYF(0));
398 break;
399 case DB_CORRUPTION:
400 my_error(ER_NOT_KEYFILE, MYF(0), table);
401 break;
402 16 case DB_TOO_BIG_RECORD:
403 /* We limit max record size to 16k for 64k page size. */
404 16 my_error(ER_TOO_BIG_ROWSIZE, MYF(0),
405
1/2
✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
16 srv_page_size == UNIV_PAGE_SIZE_MAX
406 ? REC_MAX_DATA_SIZE - 1
407 16 : page_get_free_space_of_empty(flags & DICT_TF_COMPACT) / 2);
408 16 break;
409 13 case DB_INVALID_NULL:
410 /* TODO: report the row, as we do for DB_DUPLICATE_KEY */
411 13 my_error(ER_INVALID_USE_OF_NULL, MYF(0));
412 13 break;
413 case DB_CANT_CREATE_GEOMETRY_OBJECT:
414 my_error(ER_CANT_CREATE_GEOMETRY_OBJECT, MYF(0));
415 break;
416 3 case DB_TABLESPACE_EXISTS:
417 3 my_error(ER_TABLESPACE_EXISTS, MYF(0), table);
418 3 break;
419
420 #ifdef UNIV_DEBUG
421 case DB_SUCCESS:
422 case DB_DUPLICATE_KEY:
423 case DB_ONLINE_LOG_TOO_BIG:
424 /* These codes should not be passed here. */
425 ut_error;
426 #endif /* UNIV_DEBUG */
427 52 default:
428 52 my_error(ER_GET_ERRNO, MYF(0), error, "InnoDB error");
429 52 break;
430 }
431 118 }
432
433 /** Determine if fulltext indexes exist in a given table.
434 @param table MySQL table
435 @return whether fulltext indexes exist on the table */
436 121612 static bool innobase_fulltext_exist(const TABLE *table) {
437
2/2
✓ Branch 0 taken 151025 times.
✓ Branch 1 taken 120427 times.
271452 for (uint i = 0; i < table->s->keys; i++) {
438
2/2
✓ Branch 0 taken 1185 times.
✓ Branch 1 taken 149840 times.
151025 if (table->key_info[i].flags & HA_FULLTEXT) {
439 1185 return (true);
440 }
441 }
442
443 120427 return (false);
444 }
445
446 /** Determine if spatial indexes exist in a given table.
447 @param table MySQL table
448 @return whether spatial indexes exist on the table */
449 29421 static bool innobase_spatial_exist(const TABLE *table) {
450
2/2
✓ Branch 0 taken 30601 times.
✓ Branch 1 taken 29363 times.
59964 for (uint i = 0; i < table->s->keys; i++) {
451
2/2
✓ Branch 0 taken 58 times.
✓ Branch 1 taken 30543 times.
30601 if (table->key_info[i].flags & HA_SPATIAL) {
452 58 return (true);
453 }
454 }
455
456 29363 return (false);
457 }
458
459 /** Get col in new table def of renamed column.
460 @param[in] ha_alter_info inplace alter info
461 @param[in] old_dd_column column in old table
462 @param[in] new_dd_tab new table definition
463 @return column if renamed, NULL otherwise */
464 static dd::Column *get_renamed_col(const Alter_inplace_info *ha_alter_info,
465 const dd::Column *old_dd_column,
466 const dd::Table *new_dd_tab) {
467 List_iterator_fast<Create_field> cf_it(
468 ha_alter_info->alter_info->create_list);
469 cf_it.rewind();
470 Create_field *cf;
471 while ((cf = cf_it++) != nullptr) {
472 if (cf->field && cf->field->is_flag_set(FIELD_IS_RENAMED) &&
473 strcmp(cf->change, old_dd_column->name().c_str()) == 0) {
474 /* This column is being renamed */
475 return (const_cast<dd::Column *>(
476 dd_find_column(&new_dd_tab->table(), cf->field_name)));
477 }
478 }
479
480 return nullptr;
481 }
482
483 /** Copy metadata of dd::Table and dd::Columns from old table to new table.
484 This is done during inplce alter table when table is not rebuilt.
485 @param[in] ha_alter_info inplace alter info
486 @param[in] old_dd_tab old table definition
487 @param[in,out] new_dd_tab new table definition */
488 22469 static void dd_inplace_alter_copy_instant_metadata(
489 const Alter_inplace_info *ha_alter_info, const dd::Table *old_dd_tab,
490 dd::Table *new_dd_tab) {
491
2/2
✓ Branch 0 taken 22380 times.
✓ Branch 1 taken 89 times.
22469 if (!dd_table_has_row_versions(*old_dd_tab)) {
492 22380 return;
493 }
494
495 /* Copy col phy pos from old DD table to new DD table */
496
6/10
✓ Branch 0 taken 89 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 89 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 89 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 696 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 696 times.
✓ Branch 9 taken 89 times.
785 for (auto old_dd_column : old_dd_tab->columns()) {
497 696 const char *s = dd_column_key_strings[DD_INSTANT_VERSION_DROPPED];
498
5/8
✓ Branch 0 taken 696 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 696 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 696 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 62 times.
✓ Branch 7 taken 634 times.
696 if (old_dd_column->se_private_data().exists(s)) {
499 62 uint32_t v_dropped = UINT32_UNDEFINED;
500
3/6
✓ Branch 0 taken 62 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 62 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 62 times.
✗ Branch 5 not taken.
62 old_dd_column->se_private_data().get(s, &v_dropped);
501
1/2
✓ Branch 0 taken 62 times.
✗ Branch 1 not taken.
62 if (v_dropped > 0) {
502 /* Dropped column will be copied after the loop. Skip for now. */
503 62 continue;
504 }
505 }
506
507 /* Get corresponding dd::column in new table */
508 dd::Column *new_dd_column = const_cast<dd::Column *>(
509
2/4
✓ Branch 0 taken 634 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 634 times.
✗ Branch 3 not taken.
634 dd_find_column(new_dd_tab, old_dd_column->name().c_str()));
510
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 634 times.
634 if (new_dd_column == nullptr) {
511 /* This column might have been renamed */
512 new_dd_column = get_renamed_col(ha_alter_info, old_dd_column, new_dd_tab);
513 }
514
515
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 634 times.
634 if (new_dd_column == nullptr) {
516 /* This column must have been dropped */
517 continue;
518 }
519
520
3/4
✓ Branch 0 taken 634 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
✓ Branch 3 taken 628 times.
634 if (new_dd_column->is_virtual()) {
521 6 continue;
522 }
523
524 2770 auto fn = [&](const char *s, auto &value) {
525
4/6
✓ Branch 0 taken 1385 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1385 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 882 times.
✓ Branch 5 taken 503 times.
2770 if (old_dd_column->se_private_data().exists(s)) {
526
2/4
✓ Branch 0 taken 882 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 882 times.
✗ Branch 3 not taken.
1764 old_dd_column->se_private_data().get(s, &value);
527
2/4
✓ Branch 0 taken 882 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 882 times.
✗ Branch 3 not taken.
1764 new_dd_column->se_private_data().set(s, value);
528 }
529 3398 };
530
531 /* Copy phy pos for column */
532 628 uint32_t phy_pos = UINT32_UNDEFINED;
533 628 s = dd_column_key_strings[DD_INSTANT_PHYSICAL_POS];
534
4/8
✓ Branch 0 taken 628 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 628 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 628 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 628 times.
628 ut_ad(old_dd_column->se_private_data().exists(s));
535
1/2
✓ Branch 0 taken 628 times.
✗ Branch 1 not taken.
628 fn(s, phy_pos);
536
537 /* copy version added */
538 628 uint32_t v_added = UINT32_UNDEFINED;
539 628 s = dd_column_key_strings[DD_INSTANT_VERSION_ADDED];
540
1/2
✓ Branch 0 taken 628 times.
✗ Branch 1 not taken.
628 fn(s, v_added);
541
542 /* Copy instant default values for INSTANT ADD columns */
543 628 s = dd_column_key_strings[DD_INSTANT_COLUMN_DEFAULT_NULL];
544
5/8
✓ Branch 0 taken 628 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 628 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 628 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 37 times.
✓ Branch 7 taken 591 times.
628 if (old_dd_column->se_private_data().exists(s)) {
545
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 37 times.
37 ut_ad(v_added > 0);
546 37 bool value = false;
547
1/2
✓ Branch 0 taken 37 times.
✗ Branch 1 not taken.
37 fn(s, value);
548 } else {
549 591 s = dd_column_key_strings[DD_INSTANT_COLUMN_DEFAULT];
550
5/8
✓ Branch 0 taken 591 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 591 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 591 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 92 times.
✓ Branch 7 taken 499 times.
591 if (old_dd_column->se_private_data().exists(s)) {
551
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 92 times.
92 ut_ad(v_added > 0);
552 92 dd::String_type value;
553
1/2
✓ Branch 0 taken 92 times.
✗ Branch 1 not taken.
92 fn(s, value);
554 92 } else {
555 /* This columns is not INSTANT ADD */
556
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 499 times.
499 ut_ad(v_added == UINT32_UNDEFINED);
557 }
558 }
559 }
560
561
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 81 times.
89 if (dd_table_has_instant_drop_cols(*old_dd_tab)) {
562 /* Add INSTANT dropped column from old_dd_tab to new_dd_tab */
563 8 copy_dropped_columns(old_dd_tab, new_dd_tab, UINT32_UNDEFINED);
564 }
565 }
566
567 /** Check if virtual column in old and new table are in order, excluding
568 those dropped column. This is needed because when we drop a virtual column,
569 ALTER_VIRTUAL_COLUMN_ORDER is also turned on, so we can't decide if this
570 is a real ORDER change or just DROP COLUMN
571 @param[in] table old TABLE
572 @param[in] altered_table new TABLE
573 @param[in] ha_alter_info Structure describing changes to be done
574 by ALTER TABLE and holding data used during in-place alter.
575 @return true is all columns in order, false otherwise. */
576 45933 static bool check_v_col_in_order(const TABLE *table, const TABLE *altered_table,
577 const Alter_inplace_info *ha_alter_info) {
578 45933 ulint j = 0;
579
580 /* We don't support any adding new virtual column before
581 existed virtual column. */
582
2/2
✓ Branch 0 taken 610 times.
✓ Branch 1 taken 45323 times.
45933 if (ha_alter_info->handler_flags & Alter_inplace_info::ADD_VIRTUAL_COLUMN) {
583 610 bool has_new = false;
584
585 List_iterator_fast<Create_field> cf_it(
586
1/2
✓ Branch 0 taken 610 times.
✗ Branch 1 not taken.
610 ha_alter_info->alter_info->create_list);
587
588 610 cf_it.rewind();
589
590
2/2
✓ Branch 0 taken 2723 times.
✓ Branch 1 taken 603 times.
3326 while (const Create_field *new_field = cf_it++) {
591
2/2
✓ Branch 0 taken 1671 times.
✓ Branch 1 taken 1052 times.
2723 if (!new_field->is_virtual_gcol()) {
592 /* We do not support add virtual col
593 before autoinc column */
594
3/4
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1669 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
1671 if (has_new && (new_field->flags & AUTO_INCREMENT_FLAG)) {
595 7 return (false);
596 }
597 1671 continue;
598 }
599
600 /* Found a new added virtual column. */
601
2/2
✓ Branch 0 taken 650 times.
✓ Branch 1 taken 402 times.
1052 if (!new_field->field) {
602 650 has_new = true;
603 650 continue;
604 }
605
606 /* If there's any old virtual column
607 after the new added virtual column,
608 order must be changed. */
609
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 395 times.
402 if (has_new) {
610 7 return (false);
611 }
612 2716 }
613 }
614
615 /* directly return true if ALTER_VIRTUAL_COLUMN_ORDER is not on */
616
2/2
✓ Branch 0 taken 45879 times.
✓ Branch 1 taken 47 times.
45926 if (!(ha_alter_info->handler_flags &
617 Alter_inplace_info::ALTER_VIRTUAL_COLUMN_ORDER)) {
618 45879 return (true);
619 }
620
621
2/2
✓ Branch 0 taken 368 times.
✓ Branch 1 taken 46 times.
414 for (ulint i = 0; i < table->s->fields; i++) {
622 368 Field *field = table->s->field[i];
623 368 bool dropped = false;
624
625
2/2
✓ Branch 0 taken 208 times.
✓ Branch 1 taken 160 times.
368 if (field->stored_in_db) {
626 208 continue;
627 }
628
629
3/6
✓ Branch 0 taken 160 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 160 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 160 times.
160 ut_ad(innobase_is_v_fld(field));
630
631 /* Check if this column is in drop list */
632
2/2
✓ Branch 0 taken 231 times.
✓ Branch 1 taken 113 times.
344 for (const Alter_drop *drop : ha_alter_info->alter_info->drop_list) {
633
4/4
✓ Branch 0 taken 174 times.
✓ Branch 1 taken 57 times.
✓ Branch 2 taken 47 times.
✓ Branch 3 taken 184 times.
405 if (drop->type == Alter_drop::COLUMN &&
634
2/2
✓ Branch 0 taken 47 times.
✓ Branch 1 taken 127 times.
174 my_strcasecmp(system_charset_info, field->field_name, drop->name) ==
635 0) {
636 47 dropped = true;
637 47 break;
638 }
639 }
640
641
2/2
✓ Branch 0 taken 47 times.
✓ Branch 1 taken 113 times.
160 if (dropped) {
642 47 continue;
643 }
644
645 /* Now check if the next virtual column in altered table
646 matches this column */
647
1/2
✓ Branch 0 taken 292 times.
✗ Branch 1 not taken.
292 while (j < altered_table->s->fields) {
648 292 Field *new_field = altered_table->s->field[j];
649
650
2/2
✓ Branch 0 taken 179 times.
✓ Branch 1 taken 113 times.
292 if (new_field->stored_in_db) {
651 179 j++;
652 179 continue;
653 }
654
655 113 if (my_strcasecmp(system_charset_info, field->field_name,
656
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 112 times.
113 new_field->field_name) != 0) {
657 /* different column */
658 1 return (false);
659 } else {
660 112 j++;
661 112 break;
662 }
663 }
664
665
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 112 times.
112 if (j > altered_table->s->fields) {
666 /* there should not be less column in new table
667 without them being in drop list */
668 ut_d(ut_error);
669 ut_o(return (false));
670 }
671 }
672
673 46 return (true);
674 }
675
676 /** Drop the statistics for a specified table, and mark it as discard
677 after DDL
678 @param[in,out] thd THD object
679 @param[in,out] table InnoDB table object */
680 2654 void innobase_discard_table(THD *thd, dict_table_t *table) {
681 char errstr[ERROR_STR_LENGTH];
682
2/4
✓ Branch 0 taken 2654 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 2654 times.
2654 if (dict_stats_drop_table(table->name.m_name, errstr, sizeof(errstr)) !=
683 DB_SUCCESS) {
684 push_warning_printf(thd, Sql_condition::SL_WARNING, ER_ALTER_INFO,
685 "Deleting persistent statistics"
686 " for table '%s' in"
687 " InnoDB failed: %s",
688 table->name.m_name, errstr);
689 }
690
691 2654 table->discard_after_ddl = true;
692 2654 }
693
694 /* To check if renaming a column is ok.
695 @return true if Ok, false otherwise */
696 530 static bool ok_to_rename_column(const Alter_inplace_info *ha_alter_info,
697 const TABLE *old_table,
698 const TABLE *altered_table,
699 const dict_table_t *dict_table, bool instant,
700 bool report_error) {
701 List_iterator_fast<Create_field> cf_it(
702
1/2
✓ Branch 0 taken 530 times.
✗ Branch 1 not taken.
530 ha_alter_info->alter_info->create_list);
703
704
2/2
✓ Branch 0 taken 1580 times.
✓ Branch 1 taken 524 times.
2104 for (Field **fp = old_table->field; *fp; fp++) {
705
2/2
✓ Branch 0 taken 999 times.
✓ Branch 1 taken 581 times.
1580 if (!(*fp)->is_flag_set(FIELD_IS_RENAMED)) {
706 999 continue;
707 }
708
709 581 const char *name = nullptr;
710
711 581 cf_it.rewind();
712
1/2
✓ Branch 0 taken 1114 times.
✗ Branch 1 not taken.
1114 while (const Create_field *cf = cf_it++) {
713
2/2
✓ Branch 0 taken 581 times.
✓ Branch 1 taken 533 times.
1114 if (cf->field == *fp) {
714 581 name = cf->field_name;
715 581 goto check_if_ok_to_rename;
716 }
717 533 }
718
719 ut_error;
720 581 check_if_ok_to_rename:
721 /* Prohibit renaming a column from FTS_DOC_ID
722 if full-text indexes exist. */
723
1/2
✓ Branch 0 taken 581 times.
✗ Branch 1 not taken.
581 if (!my_strcasecmp(system_charset_info, (*fp)->field_name,
724
6/6
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 576 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 3 times.
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 579 times.
586 FTS_DOC_ID_COL_NAME) &&
725 5 innobase_fulltext_exist(altered_table)) {
726
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (report_error) {
727 my_error(ER_INNODB_FT_WRONG_DOCID_COLUMN, MYF(0), name);
728 }
729 2 return false;
730 }
731
732 /* Prohibit renaming a column to an internal column. */
733 579 const char *s = dict_table->col_names;
734 unsigned j;
735 /* Skip user columns.
736 MySQL should have checked these already.
737 We want to allow renaming of c1 to c2, c2 to c1. */
738
2/2
✓ Branch 0 taken 1739 times.
✓ Branch 1 taken 579 times.
2318 for (j = 0; j < old_table->s->fields; j++) {
739
3/4
✓ Branch 0 taken 84 times.
✓ Branch 1 taken 1655 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 84 times.
1739 if (!innobase_is_v_fld(old_table->field[j])) {
740 1655 s += strlen(s) + 1;
741 }
742 }
743
744
2/2
✓ Branch 0 taken 1714 times.
✓ Branch 1 taken 575 times.
2289 for (; j < dict_table->n_def; j++) {
745
3/4
✓ Branch 0 taken 1714 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✓ Branch 3 taken 1710 times.
1714 if (!my_strcasecmp(system_charset_info, name, s)) {
746
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
4 if (report_error) {
747
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 my_error(ER_WRONG_COLUMN_NAME, MYF(0), s);
748 }
749 4 return false;
750 }
751
752 1710 s += strlen(s) + 1;
753 }
754 }
755
756 /* If column being renamed is being referenced by any other table, don't
757 allow INSTANT in that case. */
758
2/2
✓ Branch 0 taken 423 times.
✓ Branch 1 taken 101 times.
524 if (instant) {
759
2/2
✓ Branch 0 taken 34 times.
✓ Branch 1 taken 389 times.
423 if (!dict_table->referenced_set.empty()) {
760 List_iterator_fast<Create_field> cf_it(
761
1/2
✓ Branch 0 taken 34 times.
✗ Branch 1 not taken.
34 ha_alter_info->alter_info->create_list);
762
763
1/2
✓ Branch 0 taken 56 times.
✗ Branch 1 not taken.
56 for (Field **fp = old_table->field; *fp; fp++) {
764
2/2
✓ Branch 0 taken 22 times.
✓ Branch 1 taken 34 times.
56 if (!(*fp)->is_flag_set(FIELD_IS_RENAMED)) {
765 22 continue;
766 }
767
768 34 const char *col_name = (*fp)->field_name;
769
770 34 for (dict_foreign_set::iterator it = dict_table->referenced_set.begin();
771
1/2
✓ Branch 0 taken 34 times.
✗ Branch 1 not taken.
34 it != dict_table->referenced_set.end(); ++it) {
772 34 dict_foreign_t *foreign = *it;
773 34 const char *r_name = foreign->referenced_col_names[0];
774
775
1/2
✓ Branch 0 taken 34 times.
✗ Branch 1 not taken.
34 for (size_t i = 0; i < foreign->n_fields; ++i) {
776
2/4
✓ Branch 0 taken 34 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 34 times.
✗ Branch 3 not taken.
34 if (!my_strcasecmp(system_charset_info, r_name, col_name)) {
777
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 34 times.
34 if (report_error) {
778 my_error(ER_ALTER_OPERATION_NOT_SUPPORTED_REASON, MYF(0),
779 "ALGORITHM=INSTANT",
780 innobase_get_err_msg(
781 ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_FK_RENAME),
782 "ALGORITHM=INPLACE");
783 }
784 34 return false;
785 }
786 r_name = foreign->referenced_col_names[i];
787 } /* each column in reference element */
788 } /* each element in reference set */
789 } /* each column being renamed */
790 }
791 }
792
793 490 return true;
794 }
795
796 /** Determine if one ALTER TABLE can be done instantly on the table
797 @param[in] ha_alter_info The DDL operation
798 @param[in] table InnoDB table
799 @param[in] old_table old TABLE
800 @param[in] altered_table new TABLE
801 @return Instant_Type accordingly */
802 70143 static inline Instant_Type innobase_support_instant(
803 const Alter_inplace_info *ha_alter_info, const dict_table_t *table,
804 const TABLE *old_table, const TABLE *altered_table) {
805
2/2
✓ Branch 0 taken 8670 times.
✓ Branch 1 taken 61473 times.
70143 if (!(ha_alter_info->handler_flags & ~INNOBASE_INPLACE_IGNORE)) {
806 8670 return (Instant_Type::INSTANT_NO_CHANGE);
807 }
808
809 61473 Alter_inplace_info::HA_ALTER_FLAGS alter_inplace_flags =
810 61473 ha_alter_info->handler_flags & ~INNOBASE_INPLACE_IGNORE;
811
812
2/2
✓ Branch 0 taken 46768 times.
✓ Branch 1 taken 14705 times.
61473 if (alter_inplace_flags & ~INNOBASE_INSTANT_ALLOWED) {
813 46768 return (Instant_Type::INSTANT_IMPOSSIBLE);
814 }
815
816 /* During upgrade, if columns are added in system tables, avoid instant */
817
2/2
✓ Branch 0 taken 1673 times.
✓ Branch 1 taken 13032 times.
14705 if (current_thd->is_server_upgrade_thread()) {
818 1673 return (Instant_Type::INSTANT_IMPOSSIBLE);
819 }
820
821 enum class INSTANT_OPERATION {
822 COLUMN_RENAME_ONLY, /*!< Only column RENAME */
823 VIRTUAL_ADD_DROP_ONLY, /*!< Only virtual column ADD AND DROP */
824 VIRTUAL_ADD_DROP_WITH_RENAME, /*!< Virtual column ADD/DROP with RENAME */
825 INSTANT_ADD, /*< INSTANT ADD possibly with virtual column ADD and
826 column RENAME */
827 INSTANT_DROP, /*|< INSTANT DROP possibly with virtual column ADD/DROP and
828 column RENAME */
829 NONE
830 };
831
832 13032 enum INSTANT_OPERATION op = INSTANT_OPERATION::NONE;
833
834
2/2
✓ Branch 0 taken 427 times.
✓ Branch 1 taken 12605 times.
13032 if (!(alter_inplace_flags & ~Alter_inplace_info::ALTER_COLUMN_NAME)) {
835 427 op = INSTANT_OPERATION::COLUMN_RENAME_ONLY;
836
2/2
✓ Branch 0 taken 159 times.
✓ Branch 1 taken 12446 times.
12605 } else if (!(alter_inplace_flags &
837 ~(Alter_inplace_info::ADD_VIRTUAL_COLUMN |
838 Alter_inplace_info::DROP_VIRTUAL_COLUMN))) {
839 159 op = INSTANT_OPERATION::VIRTUAL_ADD_DROP_ONLY;
840
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 12446 times.
12446 } else if (!(alter_inplace_flags &
841 ~(Alter_inplace_info::ADD_VIRTUAL_COLUMN |
842 Alter_inplace_info::DROP_VIRTUAL_COLUMN |
843 Alter_inplace_info::ALTER_COLUMN_NAME))) {
844 op = INSTANT_OPERATION::VIRTUAL_ADD_DROP_WITH_RENAME;
845
2/2
✓ Branch 0 taken 9736 times.
✓ Branch 1 taken 2710 times.
12446 } else if (alter_inplace_flags & Alter_inplace_info::ADD_STORED_BASE_COLUMN &&
846
1/2
✓ Branch 0 taken 9736 times.
✗ Branch 1 not taken.
9736 !(alter_inplace_flags & Alter_inplace_info::DROP_VIRTUAL_COLUMN)) {
847 9736 op = INSTANT_OPERATION::INSTANT_ADD;
848
2/2
✓ Branch 0 taken 2643 times.
✓ Branch 1 taken 67 times.
2710 } else if (alter_inplace_flags & Alter_inplace_info::DROP_STORED_COLUMN) {
849 2643 op = INSTANT_OPERATION::INSTANT_DROP;
850 }
851
852
5/7
✓ Branch 0 taken 427 times.
✓ Branch 1 taken 159 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2643 times.
✓ Branch 4 taken 9736 times.
✓ Branch 5 taken 67 times.
✗ Branch 6 not taken.
13032 switch (op) {
853 427 case INSTANT_OPERATION::COLUMN_RENAME_ONLY: {
854 427 bool report_error = (ha_alter_info->alter_info->requested_algorithm ==
855 Alter_info::ALTER_TABLE_ALGORITHM_INSTANT);
856
2/2
✓ Branch 0 taken 389 times.
✓ Branch 1 taken 38 times.
427 if (ok_to_rename_column(ha_alter_info, old_table, altered_table, table,
857 true, report_error)) {
858 389 return (Instant_Type::INSTANT_COLUMN_RENAME);
859 }
860 38 } break;
861 159 case INSTANT_OPERATION::VIRTUAL_ADD_DROP_ONLY:
862
2/2
✓ Branch 0 taken 158 times.
✓ Branch 1 taken 1 times.
159 if (check_v_col_in_order(old_table, altered_table, ha_alter_info)) {
863 158 return (Instant_Type::INSTANT_VIRTUAL_ONLY);
864 }
865 1 break;
866 case INSTANT_OPERATION::VIRTUAL_ADD_DROP_WITH_RENAME:
867 /* Not supported yet in INPLACE. So not supporting here as well. */
868 break;
869 2643 case INSTANT_OPERATION::INSTANT_DROP:
870 /* Disable INSTANT DROP unless explicitly requested. So ALGORITHM=DEFAULT
871 will use INPLACE/COPY */
872
2/2
✓ Branch 0 taken 1364 times.
✓ Branch 1 taken 1279 times.
2643 if (ha_alter_info->alter_info->requested_algorithm ==
873 Alter_info::ALTER_TABLE_ALGORITHM_DEFAULT) {
874 1364 break;
875 }
876
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1279 times.
1279 if (!check_v_col_in_order(old_table, altered_table, ha_alter_info)) {
877 break;
878 }
879 [[fallthrough]];
880 case INSTANT_OPERATION::INSTANT_ADD:
881
882 /* Disable INSTANT DROP unless explicitly requested. So ALGORITHM=DEFAULT
883 will use INPLACE/COPY */
884
2/2
✓ Branch 0 taken 6716 times.
✓ Branch 1 taken 4299 times.
11015 if (ha_alter_info->alter_info->requested_algorithm ==
885 Alter_info::ALTER_TABLE_ALGORITHM_DEFAULT) {
886 6716 break;
887 }
888 /* If it's an ADD COLUMN without changing existing stored column orders
889 (change trailing virtual column orders is fine, especially for supporting
890 adding stored columns to a table with functional indexes), or including
891 ADD VIRTUAL COLUMN */
892
2/2
✓ Branch 0 taken 4271 times.
✓ Branch 1 taken 28 times.
4299 if (table->support_instant_add_drop()) {
893 4271 return (Instant_Type::INSTANT_ADD_DROP_COLUMN);
894 }
895 28 break;
896 67 case INSTANT_OPERATION::NONE:
897 67 break;
898 }
899
900 8214 return (Instant_Type::INSTANT_IMPOSSIBLE);
901 }
902
903 /** Determine if this is an instant ALTER TABLE.
904 This can be checked in *inplace_alter_table() functions, which are called
905 after check_if_supported_inplace_alter()
906 @param[in] ha_alter_info The DDL operation
907 @return whether it's an instant ALTER TABLE */
908 515392 static inline bool is_instant(const Alter_inplace_info *ha_alter_info) {
909 515392 return (ha_alter_info->handler_trivial_ctx !=
910 515392 instant_type_to_int(Instant_Type::INSTANT_IMPOSSIBLE));
911 }
912
913 /** Determine if ALTER TABLE needs to rebuild the table.
914 @param[in] ha_alter_info The DDL operation
915 @param[in] old_table the table we are changing
916 @param[in] is_file_per_table true if table is file_per_table
917 @return whether it is necessary to rebuild the table */
918 238989 [[nodiscard]] static bool innobase_need_rebuild(
919 const Alter_inplace_info *ha_alter_info, const TABLE *old_table,
920 bool is_file_per_table) {
921
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 238981 times.
238989 if (is_instant(ha_alter_info)) {
922 8 return (false);
923 }
924
925 238981 Alter_inplace_info::HA_ALTER_FLAGS alter_inplace_flags =
926 238981 ha_alter_info->handler_flags & ~(INNOBASE_INPLACE_IGNORE);
927
928 const bool was_none_explicitly_specified{
929 238981 Encryption::none_explicitly_specified(old_table->s->explicit_encryption,
930 238981 old_table->s->encrypt_type.str)};
931
932 238981 const bool is_none_explicitly_specified{Encryption::none_explicitly_specified(
933 238981 ha_alter_info->create_info->explicit_encryption,
934 238981 ha_alter_info->create_info->encrypt_type.str)};
935
936
4/4
✓ Branch 0 taken 9954 times.
✓ Branch 1 taken 149135 times.
✓ Branch 2 taken 2774 times.
✓ Branch 3 taken 7180 times.
159089 if ((!was_none_explicitly_specified && is_none_explicitly_specified &&
937
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 231801 times.
231801 is_file_per_table) ||
938 231801 (Encryption::is_keyring(ha_alter_info->create_info->encrypt_type.str) &&
939
4/6
✓ Branch 0 taken 159089 times.
✓ Branch 1 taken 79892 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 7180 times.
✓ Branch 5 taken 231801 times.
477962 !Encryption::is_keyring(old_table->s->encrypt_type.str)) ||
940 231801 ha_alter_info->create_info->encryption_key_id !=
941
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 231801 times.
231801 old_table->s->encryption_key_id)
942 7180 return true;
943
944
2/2
✓ Branch 0 taken 105054 times.
✓ Branch 1 taken 126747 times.
231801 if (alter_inplace_flags == Alter_inplace_info::CHANGE_CREATE_OPTION &&
945
2/2
✓ Branch 0 taken 37206 times.
✓ Branch 1 taken 67848 times.
105054 !(ha_alter_info->create_info->used_fields &
946 (HA_CREATE_USED_ROW_FORMAT | HA_CREATE_USED_KEY_BLOCK_SIZE |
947 HA_CREATE_USED_TABLESPACE))) {
948 /* Any other CHANGE_CREATE_OPTION than changing
949 ROW_FORMAT, KEY_BLOCK_SIZE or TABLESPACE can be done
950 without rebuilding the table. */
951 37206 return (false);
952 }
953
954 194595 return (!!(ha_alter_info->handler_flags & INNOBASE_ALTER_REBUILD));
955 }
956
957 /** Check if InnoDB supports a particular alter table in-place
958 @param altered_table TABLE object for new version of table.
959 @param ha_alter_info Structure describing changes to be done
960 by ALTER TABLE and holding data used during in-place alter.
961
962 @retval HA_ALTER_INPLACE_NOT_SUPPORTED Not supported
963 @retval HA_ALTER_INPLACE_NO_LOCK Supported
964 @retval HA_ALTER_INPLACE_SHARED_LOCK_AFTER_PREPARE Supported, but requires
965 lock during main phase and exclusive lock during prepare phase.
966 @retval HA_ALTER_INPLACE_NO_LOCK_AFTER_PREPARE Supported, prepare phase
967 requires exclusive lock (any transactions that have accessed the table
968 must commit or roll back first, and no transactions can access the table
969 while prepare_inplace_alter_table() is executing)
970 */
971 69704 enum_alter_inplace_result ha_innobase::check_if_supported_inplace_alter(
972 TABLE *altered_table, Alter_inplace_info *ha_alter_info) {
973
1/2
✓ Branch 0 taken 69704 times.
✗ Branch 1 not taken.
69704 DBUG_TRACE;
974
975
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 69704 times.
69704 if (srv_sys_space.created_new_raw()) {
976 return HA_ALTER_INPLACE_NOT_SUPPORTED;
977 }
978
979
3/4
✓ Branch 0 taken 69694 times.
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 69694 times.
69704 if (high_level_read_only || srv_force_recovery) {
980
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 4 times.
10 if (srv_force_recovery) {
981
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
6 my_error(ER_INNODB_FORCED_RECOVERY, MYF(0));
982 } else {
983
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 my_error(ER_READ_ONLY_MODE, MYF(0));
984 }
985 10 return HA_ALTER_ERROR;
986 }
987
988
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 69686 times.
69694 if (altered_table->s->fields > REC_MAX_N_USER_FIELDS) {
989 /* Deny the inplace ALTER TABLE. MySQL will try to
990 re-create the table and ha_innobase::create() will
991 return an error too. This is how we effectively
992 deny adding too many columns to a table. */
993 8 ha_alter_info->unsupported_reason =
994
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 innobase_get_err_msg(ER_TOO_MANY_FIELDS);
995 8 return HA_ALTER_INPLACE_NOT_SUPPORTED;
996 }
997
998 /* We don't support change Master key encryption attribute with
999 inplace algorithm. */
1000 69686 char *old_encryption = this->table->s->encrypt_type.str;
1001 69686 char *new_encryption = altered_table->s->encrypt_type.str;
1002
1003
2/2
✓ Branch 0 taken 262 times.
✓ Branch 1 taken 69424 times.
139372 if (Encryption::is_master_key_encryption(old_encryption) !=
1004 69686 Encryption::is_master_key_encryption(new_encryption)) {
1005 262 ha_alter_info->unsupported_reason =
1006
1/2
✓ Branch 0 taken 262 times.
✗ Branch 1 not taken.
262 innobase_get_err_msg(ER_UNSUPPORTED_ALTER_ENCRYPTION_INPLACE);
1007 262 return HA_ALTER_INPLACE_NOT_SUPPORTED;
1008 }
1009
1010
1/2
✓ Branch 0 taken 69424 times.
✗ Branch 1 not taken.
69424 update_thd();
1011
1012
2/2
✓ Branch 0 taken 4306 times.
✓ Branch 1 taken 65118 times.
69424 if (ha_alter_info->handler_flags &
1013 ~(INNOBASE_INPLACE_IGNORE | INNOBASE_ALTER_NOREBUILD |
1014 INNOBASE_ALTER_REBUILD)) {
1015
2/2
✓ Branch 0 taken 4156 times.
✓ Branch 1 taken 150 times.
4306 if (ha_alter_info->handler_flags &
1016 Alter_inplace_info::ALTER_STORED_COLUMN_TYPE) {
1017
2/2
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 4146 times.
4156 if (ha_alter_info->alter_info->requested_algorithm ==
1018 Alter_info::ALTER_TABLE_ALGORITHM_INSTANT) {
1019
1/2
✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
10 ha_alter_info->unsupported_reason = innobase_get_err_msg(
1020 ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_COLUMN_TYPE_INSTANT);
1021 } else {
1022
1/2
✓ Branch 0 taken 4146 times.
✗ Branch 1 not taken.
4146 ha_alter_info->unsupported_reason = innobase_get_err_msg(
1023 ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_COLUMN_TYPE);
1024 }
1025 }
1026 4306 return HA_ALTER_INPLACE_NOT_SUPPORTED;
1027 }
1028
1029 /* Only support online add foreign key constraint when check_foreigns is
1030 turned off */
1031
2/2
✓ Branch 0 taken 264 times.
✓ Branch 1 taken 64854 times.
65118 if ((ha_alter_info->handler_flags & Alter_inplace_info::ADD_FOREIGN_KEY) &&
1032
2/2
✓ Branch 0 taken 125 times.
✓ Branch 1 taken 139 times.
264 m_prebuilt->trx->check_foreigns) {
1033 125 ha_alter_info->unsupported_reason =
1034
1/2
✓ Branch 0 taken 125 times.
✗ Branch 1 not taken.
125 innobase_get_err_msg(ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_FK_CHECK);
1035 125 return HA_ALTER_INPLACE_NOT_SUPPORTED;
1036 }
1037
1038
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 64993 times.
64993 if (altered_table->file->ht != ht) {
1039 /* Non-native partitioning table engine. No longer supported, due to
1040 implementation of native InnoDB partitioning. */
1041 return HA_ALTER_INPLACE_NOT_SUPPORTED;
1042 }
1043
1044 129986 Instant_Type instant_type = innobase_support_instant(
1045
1/2
✓ Branch 0 taken 64993 times.
✗ Branch 1 not taken.
64993 ha_alter_info, m_prebuilt->table, this->table, altered_table);
1046
1047 64993 ha_alter_info->handler_trivial_ctx =
1048 64993 instant_type_to_int(Instant_Type::INSTANT_IMPOSSIBLE);
1049
1050
3/4
✓ Branch 0 taken 64993 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 63587 times.
✓ Branch 3 taken 1406 times.
64993 if (!dict_table_is_partition(m_prebuilt->table)) {
1051
3/4
✓ Branch 0 taken 53641 times.
✓ Branch 1 taken 1018 times.
✓ Branch 2 taken 8928 times.
✗ Branch 3 not taken.
63587 switch (instant_type) {
1052 53641 case Instant_Type::INSTANT_IMPOSSIBLE:
1053 53641 break;
1054 1018 case Instant_Type::INSTANT_ADD_DROP_COLUMN:
1055
2/2
✓ Branch 0 taken 125 times.
✓ Branch 1 taken 893 times.
1018 if (ha_alter_info->alter_info->requested_algorithm ==
1056 Alter_info::ALTER_TABLE_ALGORITHM_INPLACE) {
1057 /* Still fall back to INPLACE since the behaviour is different */
1058 125 break;
1059
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 893 times.
893 } else if (m_prebuilt->table->n_def == REC_MAX_N_FIELDS) {
1060 if (ha_alter_info->alter_info->requested_algorithm ==
1061 Alter_info::ALTER_TABLE_ALGORITHM_INSTANT) {
1062 my_error(ER_TOO_MANY_FIELDS, MYF(0),
1063 m_prebuilt->table->name.m_name);
1064 return HA_ALTER_ERROR;
1065 }
1066 /* INSTANT can't be done any more. Fall back to INPLACE. */
1067 break;
1068
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 891 times.
893 } else if (m_prebuilt->table->current_row_version == MAX_ROW_VERSION) {
1069
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (ha_alter_info->alter_info->requested_algorithm ==
1070 Alter_info::ALTER_TABLE_ALGORITHM_INSTANT) {
1071 2 my_error(ER_INNODB_MAX_ROW_VERSION, MYF(0),
1072
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 m_prebuilt->table->name.m_name);
1073 2 return HA_ALTER_ERROR;
1074 }
1075
1076 /* INSTANT can't be done any more. Fall back to INPLACE. */
1077 break;
1078
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 891 times.
891 } else if (!Instant_ddl_impl<dd::Table>::is_instant_add_possible(
1079 891 ha_alter_info, table, altered_table,
1080
1/2
✓ Branch 0 taken 891 times.
✗ Branch 1 not taken.
891 m_prebuilt->table)) {
1081 if (ha_alter_info->alter_info->requested_algorithm ==
1082 Alter_info::ALTER_TABLE_ALGORITHM_INSTANT) {
1083 /* Try to find if after adding columns, any possible row stays
1084 within permissible limit. If it doesn't, return error. */
1085 my_error(ER_INNODB_INSTANT_ADD_NOT_SUPPORTED_MAX_SIZE, MYF(0));
1086 return HA_ALTER_ERROR;
1087 }
1088
1089 /* INSTANT can't be done. Fall back to INPLACE. */
1090 break;
1091
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 883 times.
891 } else if (ha_alter_info->error_if_not_empty) {
1092 /* In this case, it can't be instant because the table
1093 may not be empty. Have to fall back to INPLACE */
1094 8 break;
1095 }
1096 [[fallthrough]];
1097 case Instant_Type::INSTANT_NO_CHANGE:
1098 case Instant_Type::INSTANT_VIRTUAL_ONLY:
1099 case Instant_Type::INSTANT_COLUMN_RENAME:
1100 9811 ha_alter_info->handler_trivial_ctx = instant_type_to_int(instant_type);
1101 9811 return HA_ALTER_INPLACE_INSTANT;
1102 }
1103 }
1104
1105 /* Only support NULL -> NOT NULL change if strict table sql_mode
1106 is set. Fall back to COPY for conversion if not strict tables.
1107 In-Place will fail with an error when trying to convert
1108 NULL to a NOT NULL value. */
1109 110360 if ((ha_alter_info->handler_flags &
1110
4/4
✓ Branch 0 taken 330 times.
✓ Branch 1 taken 54850 times.
✓ Branch 2 taken 133 times.
✓ Branch 3 taken 55047 times.
55510 Alter_inplace_info::ALTER_COLUMN_NOT_NULLABLE) &&
1111
3/4
✓ Branch 0 taken 330 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 133 times.
✓ Branch 3 taken 197 times.
330 !thd_is_strict_mode(m_user_thd)) {
1112 133 ha_alter_info->unsupported_reason =
1113
1/2
✓ Branch 0 taken 133 times.
✗ Branch 1 not taken.
133 innobase_get_err_msg(ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_NOT_NULL);
1114 133 return HA_ALTER_INPLACE_NOT_SUPPORTED;
1115 }
1116
1117 /* DROP PRIMARY KEY is only allowed in combination with ADD
1118 PRIMARY KEY. */
1119
2/2
✓ Branch 0 taken 225 times.
✓ Branch 1 taken 54822 times.
55047 if ((ha_alter_info->handler_flags & (Alter_inplace_info::ADD_PK_INDEX |
1120 Alter_inplace_info::DROP_PK_INDEX)) ==
1121 Alter_inplace_info::DROP_PK_INDEX) {
1122 225 ha_alter_info->unsupported_reason =
1123
1/2
✓ Branch 0 taken 225 times.
✗ Branch 1 not taken.
225 innobase_get_err_msg(ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_NOPK);
1124 225 return HA_ALTER_INPLACE_NOT_SUPPORTED;
1125 }
1126
1127 /* If a column change from NOT NULL to NULL,
1128 and there's a implict pk on this column. the
1129 table should be rebuild. The change should
1130 only go through the "Copy" method. */
1131
2/2
✓ Branch 0 taken 196 times.
✓ Branch 1 taken 54626 times.
54822 if ((ha_alter_info->handler_flags &
1132 Alter_inplace_info::ALTER_COLUMN_NULLABLE)) {
1133 196 const uint my_primary_key = altered_table->s->primary_key;
1134
1135 /* See if MYSQL table has no pk but we do. */
1136
3/4
✓ Branch 0 taken 80 times.
✓ Branch 1 taken 116 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 196 times.
276 if (UNIV_UNLIKELY(my_primary_key >= MAX_KEY) &&
1137
2/4
✓ Branch 0 taken 80 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 80 times.
80 !row_table_got_default_clust_index(m_prebuilt->table)) {
1138 ha_alter_info->unsupported_reason =
1139 innobase_get_err_msg(ER_PRIMARY_CANT_HAVE_NULL);
1140 return HA_ALTER_INPLACE_NOT_SUPPORTED;
1141 }
1142 }
1143
1144 54822 bool add_drop_v_cols = false;
1145
1146 /* If there is add or drop virtual columns, we will support operations
1147 with these 3 options alone with inplace interface for now */
1148
2/2
✓ Branch 0 taken 766 times.
✓ Branch 1 taken 54056 times.
54822 if (ha_alter_info->handler_flags &
1149 (Alter_inplace_info::ADD_VIRTUAL_COLUMN |
1150 Alter_inplace_info::DROP_VIRTUAL_COLUMN |
1151 Alter_inplace_info::ALTER_VIRTUAL_COLUMN_ORDER)) {
1152 766 ulonglong flags = ha_alter_info->handler_flags;
1153
1154 /* TODO: uncomment the flags below, once we start to
1155 support them */
1156 766 flags &=
1157 ~(Alter_inplace_info::ADD_VIRTUAL_COLUMN |
1158 Alter_inplace_info::DROP_VIRTUAL_COLUMN |
1159 Alter_inplace_info::ALTER_VIRTUAL_COLUMN_ORDER
1160 /*
1161 | Alter_inplace_info::ALTER_STORED_COLUMN_ORDER
1162 | Alter_inplace_info::ADD_STORED_BASE_COLUMN
1163 | Alter_inplace_info::DROP_STORED_COLUMN
1164 | Alter_inplace_info::ALTER_STORED_COLUMN_ORDER
1165 | Alter_inplace_info::ADD_UNIQUE_INDEX
1166 */
1167 | Alter_inplace_info::ADD_INDEX | Alter_inplace_info::DROP_INDEX);
1168
1169 1159 if (flags != 0 ||
1170
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 393 times.
393 (altered_table->s->partition_info_str &&
1171
4/6
✓ Branch 0 taken 393 times.
✓ Branch 1 taken 373 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 380 times.
✓ Branch 5 taken 386 times.
1159 altered_table->s->partition_info_str_len) ||
1172
3/4
✓ Branch 0 taken 393 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 7 times.
✓ Branch 3 taken 386 times.
393 (!check_v_col_in_order(this->table, altered_table, ha_alter_info))) {
1173 380 ha_alter_info->unsupported_reason =
1174
1/2
✓ Branch 0 taken 380 times.
✗ Branch 1 not taken.
380 innobase_get_err_msg(ER_UNSUPPORTED_ALTER_INPLACE_ON_VIRTUAL_COLUMN);
1175 380 return HA_ALTER_INPLACE_NOT_SUPPORTED;
1176 }
1177
1178 386 add_drop_v_cols = true;
1179 }
1180
1181 /* We should be able to do the operation in-place.
1182 See if we can do it online (LOCK=NONE). */
1183 54442 bool online = true;
1184
1185 List_iterator_fast<Create_field> cf_it(
1186
1/2
✓ Branch 0 taken 54442 times.
✗ Branch 1 not taken.
54442 ha_alter_info->alter_info->create_list);
1187
1188 /* Fix the key parts. */
1189 118857 for (KEY *new_key = ha_alter_info->key_info_buffer;
1190
2/2
✓ Branch 0 taken 64416 times.
✓ Branch 1 taken 54441 times.
118857 new_key < ha_alter_info->key_info_buffer + ha_alter_info->key_count;
1191 new_key++) {
1192 /* Do not support adding/dropping a virtual column, while
1193 there is a table rebuild caused by adding a new FTS_DOC_ID */
1194
4/6
✓ Branch 0 taken 1475 times.
✓ Branch 1 taken 62941 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1475 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 64416 times.
64416 if ((new_key->flags & HA_FULLTEXT) && add_drop_v_cols &&
1195 !DICT_TF2_FLAG_IS_SET(m_prebuilt->table, DICT_TF2_FTS_HAS_DOC_ID)) {
1196 ha_alter_info->unsupported_reason =
1197 innobase_get_err_msg(ER_UNSUPPORTED_ALTER_INPLACE_ON_VIRTUAL_COLUMN);
1198 return HA_ALTER_INPLACE_NOT_SUPPORTED;
1199 }
1200
1201 158683 for (KEY_PART_INFO *key_part = new_key->key_part;
1202
2/2
✓ Branch 0 taken 94268 times.
✓ Branch 1 taken 64415 times.
158683 key_part < new_key->key_part + new_key->user_defined_key_parts;
1203 key_part++) {
1204 94268 const Create_field *new_field = nullptr;
1205
1206
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 94268 times.
94268 assert(key_part->fieldnr < altered_table->s->fields);
1207
1208 94268 cf_it.rewind();
1209
2/2
✓ Branch 0 taken 257254 times.
✓ Branch 1 taken 94268 times.
351522 for (auto fieldnr = 0; fieldnr != key_part->fieldnr + 1; fieldnr++) {
1210 257254 new_field = cf_it++;
1211
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 257254 times.
257254 assert(new_field);
1212 }
1213
1214 94268 key_part->field = altered_table->field[key_part->fieldnr];
1215 /* In some special cases InnoDB emits "false"
1216 duplicate key errors with NULL key values. Let
1217 us play safe and ensure that we can correctly
1218 print key values even in such cases. */
1219
1/2
✓ Branch 0 taken 94268 times.
✗ Branch 1 not taken.
94268 key_part->null_offset = key_part->field->null_offset();
1220 94268 key_part->null_bit = key_part->field->null_bit;
1221
1222
2/2
✓ Branch 0 taken 93934 times.
✓ Branch 1 taken 334 times.
94268 if (new_field->field) {
1223 /* This is an existing column. */
1224 93934 continue;
1225 }
1226
1227 /* This is an added column. */
1228
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 334 times.
334 assert(ha_alter_info->handler_flags & Alter_inplace_info::ADD_COLUMN);
1229
1230 /* We cannot replace a hidden FTS_DOC_ID
1231 with a user-visible FTS_DOC_ID. */
1232
4/6
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 333 times.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 334 times.
335 if (m_prebuilt->table->fts && innobase_fulltext_exist(altered_table) &&
1233
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
1 !my_strcasecmp(system_charset_info, key_part->field->field_name,
1234 FTS_DOC_ID_COL_NAME)) {
1235 ha_alter_info->unsupported_reason = innobase_get_err_msg(
1236 ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_HIDDEN_FTS);
1237 return HA_ALTER_INPLACE_NOT_SUPPORTED;
1238 }
1239
1240
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 334 times.
334 assert(((key_part->field->auto_flags & Field::NEXT_NUMBER) != 0) ==
1241 key_part->field->is_flag_set(AUTO_INCREMENT_FLAG));
1242
1243
2/2
✓ Branch 0 taken 53 times.
✓ Branch 1 taken 281 times.
334 if (key_part->field->is_flag_set(AUTO_INCREMENT_FLAG)) {
1244 /* We cannot assign an AUTO_INCREMENT
1245 column values during online ALTER. */
1246
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 53 times.
53 assert(key_part->field == altered_table->found_next_number_field);
1247
1/2
✓ Branch 0 taken 53 times.
✗ Branch 1 not taken.
53 ha_alter_info->unsupported_reason = innobase_get_err_msg(
1248 ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_AUTOINC);
1249 53 online = false;
1250 }
1251
1252
2/2
✓ Branch 0 taken 181 times.
✓ Branch 1 taken 153 times.
334 if (key_part->field->is_virtual_gcol()) {
1253 /* Do not support adding index on newly added
1254 virtual column, while there is also a drop
1255 virtual column in the same clause */
1256
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 180 times.
181 if (ha_alter_info->handler_flags &
1257 Alter_inplace_info::DROP_VIRTUAL_COLUMN) {
1258
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 ha_alter_info->unsupported_reason = innobase_get_err_msg(
1259 ER_UNSUPPORTED_ALTER_INPLACE_ON_VIRTUAL_COLUMN);
1260
1261 1 return HA_ALTER_INPLACE_NOT_SUPPORTED;
1262 }
1263
1264 180 ha_alter_info->unsupported_reason =
1265
1/2
✓ Branch 0 taken 180 times.
✗ Branch 1 not taken.
180 innobase_get_err_msg(ER_UNSUPPORTED_ALTER_ONLINE_ON_VIRTUAL_COLUMN);
1266 180 online = false;
1267 }
1268 }
1269 }
1270
1271
3/4
✓ Branch 0 taken 478 times.
✓ Branch 1 taken 53963 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 478 times.
54441 assert(!m_prebuilt->table->fts ||
1272 m_prebuilt->table->fts->doc_col <= table->s->fields);
1273
4/6
✓ Branch 0 taken 478 times.
✓ Branch 1 taken 53963 times.
✓ Branch 2 taken 478 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 478 times.
54441 assert(!m_prebuilt->table->fts || m_prebuilt->table->fts->doc_col <
1274 m_prebuilt->table->get_n_user_cols());
1275
1276
2/2
✓ Branch 0 taken 101 times.
✓ Branch 1 taken 54340 times.
54441 if (ha_alter_info->handler_flags & Alter_inplace_info::ADD_SPATIAL_INDEX) {
1277 101 ha_alter_info->unsupported_reason =
1278
1/2
✓ Branch 0 taken 101 times.
✗ Branch 1 not taken.
101 innobase_get_err_msg(ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_GIS);
1279 101 online = false;
1280 }
1281
1282
6/6
✓ Branch 0 taken 478 times.
✓ Branch 1 taken 53963 times.
✓ Branch 2 taken 374 times.
✓ Branch 3 taken 104 times.
✓ Branch 4 taken 374 times.
✓ Branch 5 taken 54067 times.
54441 if (m_prebuilt->table->fts && innobase_fulltext_exist(altered_table)) {
1283 /* FULLTEXT indexes are supposed to remain. */
1284 /* Disallow DROP INDEX FTS_DOC_ID_INDEX */
1285
1286
2/2
✓ Branch 0 taken 23 times.
✓ Branch 1 taken 374 times.
397 for (uint i = 0; i < ha_alter_info->index_drop_count; i++) {
1287
2/4
✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 23 times.
23 if (!my_strcasecmp(system_charset_info,
1288 ha_alter_info->index_drop_buffer[i]->name,
1289 FTS_DOC_ID_INDEX_NAME)) {
1290 ha_alter_info->unsupported_reason = innobase_get_err_msg(
1291 ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_CHANGE_FTS);
1292 return HA_ALTER_INPLACE_NOT_SUPPORTED;
1293 }
1294 }
1295
1296 /* InnoDB can have a hidden FTS_DOC_ID_INDEX on a
1297 visible FTS_DOC_ID column as well. Prevent dropping or
1298 renaming the FTS_DOC_ID. */
1299
1300
2/2
✓ Branch 0 taken 2518 times.
✓ Branch 1 taken 369 times.
2887 for (Field **fp = table->field; *fp; fp++) {
1301
4/4
✓ Branch 0 taken 2512 times.
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 2507 times.
✓ Branch 3 taken 11 times.
5030 if (!((*fp)->is_flag_set(FIELD_IS_RENAMED) ||
1302
2/2
✓ Branch 0 taken 2507 times.
✓ Branch 1 taken 5 times.
2512 (*fp)->is_flag_set(FIELD_IS_DROPPED))) {
1303 2507 continue;
1304 }
1305
1306
3/4
✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
✓ Branch 3 taken 6 times.
11 if (!my_strcasecmp(system_charset_info, (*fp)->field_name,
1307 FTS_DOC_ID_COL_NAME)) {
1308
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
5 ha_alter_info->unsupported_reason = innobase_get_err_msg(
1309 ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_CHANGE_FTS);
1310 5 return HA_ALTER_INPLACE_NOT_SUPPORTED;
1311 }
1312 }
1313 }
1314
1315 54436 m_prebuilt->trx->will_lock++;
1316
1317
2/2
✓ Branch 0 taken 54118 times.
✓ Branch 1 taken 318 times.
54436 if (!online) {
1318 /* We already determined that only a non-locking
1319 operation is possible. */
1320 108236 } else if (((ha_alter_info->handler_flags &
1321
2/2
✓ Branch 0 taken 27733 times.
✓ Branch 1 taken 24692 times.
52425 Alter_inplace_info::ADD_PK_INDEX) ||
1322 52425 innobase_need_rebuild(
1323 ha_alter_info, altered_table,
1324
7/8
✓ Branch 0 taken 52425 times.
✓ Branch 1 taken 1693 times.
✓ Branch 2 taken 52425 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 29388 times.
✓ Branch 5 taken 38 times.
✓ Branch 6 taken 67 times.
✓ Branch 7 taken 54051 times.
137624 dict_table_is_file_per_table(m_prebuilt->table))) &&
1325
2/2
✓ Branch 0 taken 29 times.
✓ Branch 1 taken 29359 times.
58814 (innobase_fulltext_exist(altered_table) ||
1326 29388 innobase_spatial_exist(altered_table))) {
1327 /* Refuse to rebuild the table online, if
1328 FULLTEXT OR SPATIAL indexes are to survive the rebuild. */
1329 67 online = false;
1330 /* If the table already contains fulltext indexes,
1331 refuse to rebuild the table natively altogether. */
1332
2/2
✓ Branch 0 taken 34 times.
✓ Branch 1 taken 33 times.
67 if (m_prebuilt->table->fts) {
1333 34 ha_alter_info->unsupported_reason =
1334
1/2
✓ Branch 0 taken 34 times.
✗ Branch 1 not taken.
34 innobase_get_err_msg(ER_INNODB_FT_LIMIT);
1335 34 return HA_ALTER_INPLACE_NOT_SUPPORTED;
1336 }
1337
1338
2/2
✓ Branch 0 taken 29 times.
✓ Branch 1 taken 4 times.
33 if (innobase_spatial_exist(altered_table)) {
1339 29 ha_alter_info->unsupported_reason =
1340
1/2
✓ Branch 0 taken 29 times.
✗ Branch 1 not taken.
29 innobase_get_err_msg(ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_GIS);
1341 } else {
1342 4 ha_alter_info->unsupported_reason =
1343
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 innobase_get_err_msg(ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_FTS);
1344 }
1345
2/2
✓ Branch 0 taken 6282 times.
✓ Branch 1 taken 47769 times.
54051 } else if ((ha_alter_info->handler_flags & Alter_inplace_info::ADD_INDEX)) {
1346 /* Building a full-text index requires a lock.
1347 We could do without a lock if the table already contains
1348 an FTS_DOC_ID column, but in that case we would have
1349 to apply the modification log to the full-text indexes. */
1350
1351
2/2
✓ Branch 0 taken 6476 times.
✓ Branch 1 taken 5846 times.
12322 for (uint i = 0; i < ha_alter_info->index_add_count; i++) {
1352 6476 const KEY *key =
1353 6476 &ha_alter_info->key_info_buffer[ha_alter_info->index_add_buffer[i]];
1354
2/2
✓ Branch 0 taken 436 times.
✓ Branch 1 taken 6040 times.
6476 if (key->flags & HA_FULLTEXT) {
1355
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 436 times.
436 assert(!(key->flags & HA_KEYFLAG_MASK &
1356 ~(HA_FULLTEXT | HA_PACK_KEY | HA_GENERATED_KEY |
1357 HA_BINARY_PACK_KEY)));
1358 436 ha_alter_info->unsupported_reason =
1359
1/2
✓ Branch 0 taken 436 times.
✗ Branch 1 not taken.
436 innobase_get_err_msg(ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_FTS);
1360 436 online = false;
1361 436 break;
1362 }
1363 }
1364 }
1365
1366
2/2
✓ Branch 0 taken 53615 times.
✓ Branch 1 taken 787 times.
54402 return online ? HA_ALTER_INPLACE_NO_LOCK_AFTER_PREPARE
1367 54402 : HA_ALTER_INPLACE_SHARED_LOCK_AFTER_PREPARE;
1368 69704 }
1369
1370 /** Update the metadata in prepare phase. This only check if dd::Tablespace
1371 should be removed or(and) created, because to remove and store dd::Tablespace
1372 could fail, so it's better to do it earlier, to prevent a late rollback
1373 @param[in,out] thd MySQL connection
1374 @param[in] old_table Old InnoDB table object
1375 @param[in,out] new_table New InnoDB table object
1376 @param[in] old_dd_tab Old dd::Table or dd::Partition
1377 @return false On success
1378 @retval true On failure */
1379 template <typename Table>
1380 [[nodiscard]] static bool dd_prepare_inplace_alter_table(
1381 THD *thd, const dict_table_t *old_table, dict_table_t *new_table,
1382 const Table *old_dd_tab);
1383
1384 /** Update metadata in commit phase. Note this function should only update
1385 the metadata which would not result in failure
1386 @param[in] old_info Some table information for the old table
1387 @param[in,out] new_table New InnoDB table object
1388 @param[in] old_dd_tab Old dd::Table or dd::Partition
1389 @param[in,out] new_dd_tab New dd::Table or dd::Partition */
1390 template <typename Table>
1391 static void dd_commit_inplace_alter_table(
1392 const alter_table_old_info_t &old_info, dict_table_t *new_table,
1393 const Table *old_dd_tab, Table *new_dd_tab);
1394
1395 /** Update metadata in commit phase when the alter table does
1396 no change to the table
1397 @param[in] ha_alter_info the DDL operation
1398 @param[in] old_dd_tab Old dd::Table or dd::Partition
1399 @param[in] new_dd_tab New dd::Table or dd::Partition
1400 @param[in] ignore_fts ignore FTS update if true */
1401 template <typename Table>
1402 static void dd_commit_inplace_no_change(const Alter_inplace_info *ha_alter_info,
1403 const Table *old_dd_tab,
1404 Table *new_dd_tab, bool ignore_fts);
1405
1406 /** Update table level instant metadata in commit phase
1407 @param[in] table InnoDB table object
1408 @param[in] old_dd_tab old dd::Table
1409 @param[in] new_dd_tab new dd::Table */
1410 static void dd_commit_inplace_update_instant_meta(const dict_table_t *table,
1411 const dd::Table *old_dd_tab,
1412 dd::Table *new_dd_tab);
1413
1414 /** Allows InnoDB to update internal structures with concurrent
1415 writes blocked (provided that check_if_supported_inplace_alter()
1416 did not return HA_ALTER_INPLACE_NO_LOCK).
1417 This will be invoked before inplace_alter_table().
1418 @param[in] altered_table TABLE object for new version of table.
1419 @param[in,out] ha_alter_info Structure describing changes to be done
1420 by ALTER TABLE and holding data used during in-place alter.
1421 @param[in] old_dd_tab dd::Table object describing old version
1422 of the table.
1423 @param[in,out] new_dd_tab dd::Table object for the new version of
1424 the table. Can be adjusted by this call. Changes to the table definition will
1425 be persisted in the data-dictionary at statement commit time.
1426 @retval true Failure
1427 @retval false Success
1428 */
1429 62828 bool ha_innobase::prepare_inplace_alter_table(TABLE *altered_table,
1430 Alter_inplace_info *ha_alter_info,
1431 const dd::Table *old_dd_tab,
1432 dd::Table *new_dd_tab) {
1433
1/2
✓ Branch 0 taken 62828 times.
✗ Branch 1 not taken.
62828 DBUG_TRACE;
1434
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 62828 times.
62828 ut_ad(old_dd_tab != nullptr);
1435
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 62828 times.
62828 ut_ad(new_dd_tab != nullptr);
1436
1437
7/8
✓ Branch 0 taken 62828 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 7 times.
✓ Branch 3 taken 62821 times.
✓ Branch 4 taken 4 times.
✓ Branch 5 taken 3 times.
✓ Branch 6 taken 4 times.
✓ Branch 7 taken 62824 times.
62835 if (dict_sys_t::is_dd_table_id(m_prebuilt->table->id) &&
1438 7 innobase_need_rebuild(ha_alter_info, table,
1439
1/2
✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
7 dict_table_is_file_per_table(m_prebuilt->table))) {
1440
2/4
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 4 times.
4 ut_ad(!m_prebuilt->table->is_temporary());
1441
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 my_error(ER_NOT_ALLOWED_COMMAND, MYF(0));
1442 4 return true;
1443 }
1444
1445
2/2
✓ Branch 0 taken 4634 times.
✓ Branch 1 taken 58190 times.
62824 if (altered_table->found_next_number_field != nullptr) {
1446
2/4
✓ Branch 0 taken 4634 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4634 times.
✗ Branch 3 not taken.
4634 dd_copy_autoinc(old_dd_tab->se_private_data(),
1447
1/2
✓ Branch 0 taken 4634 times.
✗ Branch 1 not taken.
4634 new_dd_tab->se_private_data());
1448
1/2
✓ Branch 0 taken 4634 times.
✗ Branch 1 not taken.
4634 dd_set_autoinc(new_dd_tab->se_private_data(),
1449
1/2
✓ Branch 0 taken 4634 times.
✗ Branch 1 not taken.
4634 ha_alter_info->create_info->auto_increment_value);
1450 }
1451
1452
1/2
✓ Branch 0 taken 62775 times.
✗ Branch 1 not taken.
62824 return prepare_inplace_alter_table_impl<dd::Table>(
1453 62775 altered_table, ha_alter_info, old_dd_tab, new_dd_tab);
1454 62779 }
1455
1456 int ha_innobase::parallel_scan_init(void *&scan_ctx, size_t *num_threads,
1457 bool use_reserved_threads) {
1458 if (dict_table_is_discarded(m_prebuilt->table)) {
1459 ib_senderrf(ha_thd(), IB_LOG_LEVEL_ERROR, ER_TABLESPACE_DISCARDED,
1460 m_prebuilt->table->name.m_name);
1461
1462 return (HA_ERR_NO_SUCH_TABLE);
1463 }
1464
1465 scan_ctx = nullptr;
1466
1467 update_thd();
1468
1469 auto trx = m_prebuilt->trx;
1470
1471 innobase_register_trx(ht, ha_thd(), trx);
1472
1473 trx_start_if_not_started_xa(trx, false, UT_LOCATION_HERE);
1474
1475 trx_assign_read_view(trx);
1476
1477 size_t max_threads = thd_parallel_read_threads(m_prebuilt->trx->mysql_thd);
1478
1479 max_threads =
1480 Parallel_reader::available_threads(max_threads, use_reserved_threads);
1481
1482 if (max_threads == 0) {
1483 return (HA_ERR_GENERIC);
1484 }
1485
1486 const auto row_len = m_prebuilt->mysql_row_len;
1487
1488 auto adapter = ut::new_withkey<Parallel_reader_adapter>(
1489 UT_NEW_THIS_FILE_PSI_KEY, max_threads, row_len);
1490
1491 if (adapter == nullptr) {
1492 Parallel_reader::release_threads(max_threads);
1493 return (HA_ERR_OUT_OF_MEM);
1494 }
1495
1496 Parallel_reader::Scan_range full_scan{};
1497
1498 Parallel_reader::Config config(full_scan, m_prebuilt->table->first_index());
1499
1500 dberr_t err =
1501 adapter->add_scan(trx, config, [=](const Parallel_reader::Ctx *ctx) {
1502 return (adapter->process_rows(ctx));
1503 });
1504
1505 if (err != DB_SUCCESS) {
1506 ut::delete_(adapter);
1507 return (convert_error_code_to_mysql(err, 0, ha_thd()));
1508 }
1509
1510 scan_ctx = adapter;
1511 *num_threads = max_threads;
1512
1513 build_template(true);
1514
1515 adapter->set(m_prebuilt);
1516
1517 return (0);
1518 }
1519
1520 int ha_innobase::parallel_scan(void *scan_ctx, void **thread_ctxs,
1521 Reader::Init_fn init_fn, Reader::Load_fn load_fn,
1522 Reader::End_fn end_fn) {
1523 if (dict_table_is_discarded(m_prebuilt->table)) {
1524 ib_senderrf(ha_thd(), IB_LOG_LEVEL_ERROR, ER_TABLESPACE_DISCARDED,
1525 m_prebuilt->table->name.m_name);
1526
1527 return (HA_ERR_NO_SUCH_TABLE);
1528 }
1529
1530 ut_a(scan_ctx != nullptr);
1531
1532 update_thd();
1533
1534 build_template(true);
1535
1536 auto adapter = static_cast<Parallel_reader_adapter *>(scan_ctx);
1537
1538 auto err = adapter->run(thread_ctxs, init_fn, load_fn, end_fn);
1539
1540 return (convert_error_code_to_mysql(err, 0, ha_thd()));
1541 }
1542
1543 void ha_innobase::parallel_scan_end(void *parallel_scan_ctx) {
1544 Parallel_reader_adapter *parallel_reader =
1545 static_cast<Parallel_reader_adapter *>(parallel_scan_ctx);
1546 ut::delete_(parallel_reader);
1547 }
1548
1549 62609 bool ha_innobase::inplace_alter_table(TABLE *altered_table,
1550 Alter_inplace_info *ha_alter_info,
1551 const dd::Table *old_dd_tab
1552 [[maybe_unused]],
1553 dd::Table *new_dd_tab [[maybe_unused]]) {
1554
1/2
✓ Branch 0 taken 62609 times.
✗ Branch 1 not taken.
62609 DBUG_TRACE;
1555
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 62609 times.
62609 ut_ad(old_dd_tab != nullptr);
1556
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 62609 times.
62609 ut_ad(new_dd_tab != nullptr);
1557
1558 /* Notify clone during in place operations */
1559 Clone_notify notifier(Clone_notify::Type::SPACE_ALTER_INPLACE,
1560
1/2
✓ Branch 0 taken 62609 times.
✗ Branch 1 not taken.
62609 dict_sys_t::s_invalid_space_id, false);
1561
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 62609 times.
62609 ut_ad(!notifier.failed());
1562
1563
1/2
✓ Branch 0 taken 62609 times.
✗ Branch 1 not taken.
125218 return inplace_alter_table_impl<dd::Table>(altered_table, ha_alter_info);
1564 62609 }
1565
1566 /** Commit or rollback the changes made during
1567 prepare_inplace_alter_table() and inplace_alter_table() inside
1568 the storage engine. Note that the allowed level of concurrency
1569 during this operation will be the same as for
1570 inplace_alter_table() and thus might be higher than during
1571 prepare_inplace_alter_table(). (E.g concurrent writes were
1572 blocked during prepare, but might not be during commit).
1573 @param[in] altered_table TABLE object for new version of table.
1574 @param[in,out] ha_alter_info Structure describing changes to be done
1575 by ALTER TABLE and holding data used during in-place alter.
1576 @param[in] commit True to commit or false to rollback.
1577 @param[in] old_dd_tab dd::Table object representing old
1578 version of the table
1579 @param[in,out] new_dd_tab dd::Table object representing new
1580 version of the table. Can be adjusted by this call. Changes to the table
1581 definition will be persisted in the data-dictionary at statement
1582 commit time.
1583 @retval true Failure
1584 @retval false Success */
1585 62790 bool ha_innobase::commit_inplace_alter_table(TABLE *altered_table,
1586 Alter_inplace_info *ha_alter_info,
1587 bool commit,
1588 const dd::Table *old_dd_tab,
1589 dd::Table *new_dd_tab) {
1590
1/2
✓ Branch 0 taken 62790 times.
✗ Branch 1 not taken.
62790 DBUG_TRACE;
1591
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 62790 times.
62790 ut_ad(old_dd_tab != nullptr);
1592
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 62790 times.
62790 ut_ad(new_dd_tab != nullptr);
1593
1594 62790 ha_innobase_inplace_ctx *ctx =
1595 static_cast<ha_innobase_inplace_ctx *>(ha_alter_info->handler_ctx);
1596
1597 62790 alter_table_old_info_t old_info;
1598 62790 ut_d(bool old_info_updated = false);
1599
4/4
✓ Branch 0 taken 62065 times.
✓ Branch 1 taken 725 times.
✓ Branch 2 taken 52260 times.
✓ Branch 3 taken 9805 times.
62790 if (commit && ctx != nullptr) {
1600
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 52260 times.
52260 ut_ad(!!(ha_alter_info->handler_flags & ~INNOBASE_INPLACE_IGNORE));
1601 52260 old_info.update(ctx->old_table, ctx->need_rebuild());
1602 52260 ut_d(old_info_updated = true);
1603 }
1604
1605
1/2
✓ Branch 0 taken 62752 times.
✗ Branch 1 not taken.
62790 bool res = commit_inplace_alter_table_impl<dd::Table>(
1606 altered_table, ha_alter_info, commit, new_dd_tab);
1607
1608
4/4
✓ Branch 0 taken 62740 times.
✓ Branch 1 taken 12 times.
✓ Branch 2 taken 725 times.
✓ Branch 3 taken 62015 times.
62752 if (res || !commit) {
1609 737 return true;
1610 }
1611
1612
6/8
✓ Branch 0 taken 52210 times.
✓ Branch 1 taken 9805 times.
✓ Branch 2 taken 29629 times.
✓ Branch 3 taken 22581 times.
✓ Branch 4 taken 29629 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 62015 times.
62015 ut_ad(ctx == nullptr || !(ctx->need_rebuild() && is_instant(ha_alter_info)));
1613
1614
2/2
✓ Branch 0 taken 9805 times.
✓ Branch 1 taken 52210 times.
62015 if (is_instant(ha_alter_info)) {
1615
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 9805 times.
9805 ut_ad(!res);
1616
1617 Instant_ddl_impl<dd::Table> executor(
1618 9805 ha_alter_info, m_user_thd, m_prebuilt->trx, m_prebuilt->table, table,
1619 altered_table, old_dd_tab, new_dd_tab,
1620 9805 altered_table->found_next_number_field != nullptr
1621 490 ? &m_prebuilt->table->autoinc
1622
2/2
✓ Branch 0 taken 490 times.
✓ Branch 1 taken 9315 times.
9805 : nullptr);
1623
1624 /* Execute Instant DDL */
1625
1/2
✓ Branch 0 taken 9805 times.
✗ Branch 1 not taken.
9805 executor.commit_instant_ddl();
1626
2/4
✓ Branch 0 taken 52210 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 52210 times.
62015 } else if (!(ha_alter_info->handler_flags & ~INNOBASE_INPLACE_IGNORE) ||
1627 ctx == nullptr) {
1628 ut_ad(!res);
1629 dd_commit_inplace_no_change(ha_alter_info, old_dd_tab, new_dd_tab, false);
1630 } else {
1631
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 52210 times.
52210 ut_ad(old_info_updated);
1632
7/8
✓ Branch 0 taken 22581 times.
✓ Branch 1 taken 29629 times.
✓ Branch 2 taken 22581 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 22230 times.
✓ Branch 5 taken 351 times.
✓ Branch 6 taken 22230 times.
✓ Branch 7 taken 29980 times.
52210 if (!ctx->need_rebuild() && !dict_table_has_fts_index(m_prebuilt->table)) {
1633 /* Table is not rebuilt so copy instant metadata. */
1634
1/2
✓ Branch 0 taken 22230 times.
✗ Branch 1 not taken.
22230 dd_inplace_alter_copy_instant_metadata(ha_alter_info, old_dd_tab,
1635 new_dd_tab);
1636 }
1637
1638
1/2
✓ Branch 0 taken 52210 times.
✗ Branch 1 not taken.
52210 dd_commit_inplace_alter_table<dd::Table>(old_info, ctx->new_table,
1639 old_dd_tab, new_dd_tab);
1640
2/2
✓ Branch 0 taken 22581 times.
✓ Branch 1 taken 29629 times.
52210 if (!ctx->need_rebuild()) {
1641
1/2
✓ Branch 0 taken 22581 times.
✗ Branch 1 not taken.
22581 dd_commit_inplace_update_instant_meta(ctx->new_table, old_dd_tab,
1642 new_dd_tab);
1643 }
1644
2/4
✓ Branch 0 taken 52210 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 52210 times.
52210 ut_ad(dd_table_match(ctx->new_table, new_dd_tab));
1645 }
1646
1647 #ifdef UNIV_DEBUG
1648 /* Inplace ALTERs for expanded fast index creation can only be about
1649 DROP and ADD INDEX and never be instant operation */
1650
7/8
✓ Branch 0 taken 62015 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 636 times.
✓ Branch 3 taken 61379 times.
✓ Branch 4 taken 113 times.
✓ Branch 5 taken 523 times.
✓ Branch 6 taken 561 times.
✓ Branch 7 taken 61454 times.
62128 if (dd_table_has_instant_cols(*old_dd_tab) &&
1651
2/2
✓ Branch 0 taken 38 times.
✓ Branch 1 taken 75 times.
113 (ctx == nullptr || !ctx->need_rebuild())) {
1652
3/10
✓ Branch 0 taken 561 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 561 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✓ Branch 9 taken 561 times.
561 ut_ad(dd_table_has_instant_cols(*new_dd_tab) ||
1653 (ctx == nullptr || ctx->new_table->skip_alter_undo));
1654 }
1655 #endif /* UNIV_DEBUG */
1656
1657 62015 return res;
1658 62752 }
1659
1660 /** Initialize the dict_foreign_t structure with supplied info
1661 @return true if added, false if duplicate foreign->id */
1662 137 static bool innobase_init_foreign(
1663 dict_foreign_t *foreign, /*!< in/out: structure to
1664 initialize */
1665 const char *constraint_name, /*!< in/out: constraint name if
1666 exists */
1667 dict_table_t *table, /*!< in: foreign table */
1668 dict_index_t *index, /*!< in: foreign key index */
1669 const char **column_names, /*!< in: foreign key column
1670 names */
1671 ulint num_field, /*!< in: number of columns */
1672 const char *referenced_table_name, /*!< in: referenced table
1673 name */
1674 dict_table_t *referenced_table, /*!< in: referenced table */
1675 dict_index_t *referenced_index, /*!< in: referenced index */
1676 const char **referenced_column_names, /*!< in: referenced column
1677 names */
1678 ulint referenced_num_field) /*!< in: number of referenced
1679 columns */
1680 {
1681
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 137 times.
137 ut_ad(dict_sys_mutex_own());
1682
1683
1/2
✓ Branch 0 taken 137 times.
✗ Branch 1 not taken.
137 if (constraint_name) {
1684 ulint db_len;
1685
1686 /* Catenate 'databasename/' to the constraint name specified
1687 by the user: we conceive the constraint as belonging to the
1688 same MySQL 'database' as the table itself. We store the name
1689 to foreign->id. */
1690
1691 137 db_len = dict_get_db_name_len(table->name.m_name);
1692
1693 274 foreign->id = static_cast<char *>(
1694 137 mem_heap_alloc(foreign->heap, db_len + strlen(constraint_name) + 2));
1695
1696 137 ut_memcpy(foreign->id, table->name.m_name, db_len);
1697 137 foreign->id[db_len] = '/';
1698 137 strcpy(foreign->id + db_len + 1, constraint_name);
1699
1700 /* Check if any existing foreign key has the same id,
1701 this is needed only if user supplies the constraint name */
1702
1703
2/4
✓ Branch 0 taken 137 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 137 times.
137 if (table->foreign_set.find(foreign) != table->foreign_set.end()) {
1704 return (false);
1705 }
1706 }
1707
1708 137 foreign->foreign_table = table;
1709 274 foreign->foreign_table_name =
1710 137 mem_heap_strdup(foreign->heap, table->name.m_name);
1711 137 dict_mem_foreign_table_name_lookup_set(foreign, true);
1712
1713 137 foreign->foreign_index = index;
1714 137 foreign->n_fields = (unsigned int)num_field;
1715
1716 274 foreign->foreign_col_names = static_cast<const char **>(
1717 137 mem_heap_alloc(foreign->heap, num_field * sizeof(void *)));
1718
1719
2/2
✓ Branch 0 taken 155 times.
✓ Branch 1 taken 137 times.
292 for (ulint i = 0; i < foreign->n_fields; i++) {
1720 310 foreign->foreign_col_names[i] =
1721 155 mem_heap_strdup(foreign->heap, column_names[i]);
1722 }
1723
1724 137 foreign->referenced_index = referenced_index;
1725 137 foreign->referenced_table = referenced_table;
1726
1727 274 foreign->referenced_table_name =
1728 137 mem_heap_strdup(foreign->heap, referenced_table_name);
1729 137 dict_mem_referenced_table_name_lookup_set(foreign, true);
1730
1731 274 foreign->referenced_col_names = static_cast<const char **>(
1732 137 mem_heap_alloc(foreign->heap, referenced_num_field * sizeof(void *)));
1733
1734
2/2
✓ Branch 0 taken 155 times.
✓ Branch 1 taken 137 times.
292 for (ulint i = 0; i < foreign->n_fields; i++) {
1735 310 foreign->referenced_col_names[i] =
1736 155 mem_heap_strdup(foreign->heap, referenced_column_names[i]);
1737 }
1738
1739 137 return (true);
1740 }
1741
1742 /** Check whether the foreign key options is legit
1743 @return true if it is */
1744 137 [[nodiscard]] static bool innobase_check_fk_option(
1745 const dict_foreign_t *foreign) /*!< in: foreign key */
1746 {
1747
2/2
✓ Branch 0 taken 58 times.
✓ Branch 1 taken 79 times.
137 if (!foreign->foreign_index) {
1748 58 return (true);
1749 }
1750
1751
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 71 times.
79 if (foreign->type &
1752 (DICT_FOREIGN_ON_UPDATE_SET_NULL | DICT_FOREIGN_ON_DELETE_SET_NULL)) {
1753
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 8 times.
16 for (ulint j = 0; j < foreign->n_fields; j++) {
1754
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 if ((foreign->foreign_index->get_col(j)->prtype) & DATA_NOT_NULL) {
1755 /* It is not sensible to define
1756 SET NULL if the column is not
1757 allowed to be NULL! */
1758 return (false);
1759 }
1760 }
1761 }
1762
1763 79 return (true);
1764 }
1765
1766 /** Set foreign key options
1767 @return true if successfully set */
1768 137 [[nodiscard]] static bool innobase_set_foreign_key_option(
1769 dict_foreign_t *foreign, /*!< in:InnoDB Foreign key */
1770 const Foreign_key_spec *fk_key) /*!< in: Foreign key info from
1771 MySQL */
1772 {
1773
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 137 times.
137 ut_ad(!foreign->type);
1774
1775
4/5
✓ Branch 0 taken 13 times.
✓ Branch 1 taken 17 times.
✓ Branch 2 taken 10 times.
✓ Branch 3 taken 97 times.
✗ Branch 4 not taken.
137 switch (fk_key->delete_opt) {
1776 13 case FK_OPTION_NO_ACTION:
1777 case FK_OPTION_RESTRICT:
1778 case FK_OPTION_DEFAULT:
1779 13 foreign->type = DICT_FOREIGN_ON_DELETE_NO_ACTION;
1780 13 break;
1781 17 case FK_OPTION_CASCADE:
1782 17 foreign->type = DICT_FOREIGN_ON_DELETE_CASCADE;
1783 17 break;
1784 10 case FK_OPTION_SET_NULL:
1785 10 foreign->type = DICT_FOREIGN_ON_DELETE_SET_NULL;
1786 10 break;
1787 97 case FK_OPTION_UNDEF:
1788 97 break;
1789 }
1790
1791
3/5
✗ Branch 0 not taken.
✓ Branch 1 taken 58 times.
✓ Branch 2 taken 12 times.
✓ Branch 3 taken 67 times.
✗ Branch 4 not taken.
137 switch (fk_key->update_opt) {
1792 case FK_OPTION_NO_ACTION:
1793 case FK_OPTION_RESTRICT:
1794 case FK_OPTION_DEFAULT:
1795 foreign->type |= DICT_FOREIGN_ON_UPDATE_NO_ACTION;
1796 break;
1797 58 case FK_OPTION_CASCADE:
1798 58 foreign->type |= DICT_FOREIGN_ON_UPDATE_CASCADE;
1799 58 break;
1800 12 case FK_OPTION_SET_NULL:
1801 12 foreign->type |= DICT_FOREIGN_ON_UPDATE_SET_NULL;
1802 12 break;
1803 67 case FK_OPTION_UNDEF:
1804 67 break;
1805 }
1806
1807 137 return (innobase_check_fk_option(foreign));
1808 }
1809
1810 /** Check if a foreign key constraint can make use of an index
1811 that is being created.
1812 @return useable index, or NULL if none found */
1813 82 [[nodiscard]] static const KEY *innobase_find_equiv_index(
1814 const char *const *col_names,
1815 /*!< in: column names */
1816 uint n_cols, /*!< in: number of columns */
1817 const KEY *keys, /*!< in: index information */
1818 const uint *add, /*!< in: indexes being created */
1819 uint n_add) /*!< in: number of indexes to create */
1820 {
1821
2/2
✓ Branch 0 taken 87 times.
✓ Branch 1 taken 1 times.
88 for (uint i = 0; i < n_add; i++) {
1822 87 const KEY *key = &keys[add[i]];
1823
1824
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 87 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 87 times.
87 if (key->user_defined_key_parts < n_cols || key->flags & HA_SPATIAL) {
1825 no_match:
1826 6 continue;
1827 }
1828
1829
2/2
✓ Branch 0 taken 93 times.
✓ Branch 1 taken 81 times.
174 for (uint j = 0; j < n_cols; j++) {
1830 93 const KEY_PART_INFO &key_part = key->key_part[j];
1831 93 uint32_t col_len = key_part.field->pack_length();
1832
1833 /* Any index on virtual columns cannot be used
1834 for reference constaint */
1835
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 93 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
93 if (innobase_is_v_fld(key_part.field)) {
1836 goto no_match;
1837 }
1838
1839 /* The MySQL pack length contains 1 or 2 bytes
1840 length field for a true VARCHAR. */
1841
1842
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 92 times.
93 if (key_part.field->type() == MYSQL_TYPE_VARCHAR) {
1843 1 col_len -= key_part.field->get_length_bytes();
1844 }
1845
1846
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 93 times.
93 if (key_part.length < col_len) {
1847 /* Column prefix indexes cannot be
1848 used for FOREIGN KEY constraints. */
1849 goto no_match;
1850 }
1851
1852
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 87 times.
93 if (innobase_strcasecmp(col_names[j], key_part.field->field_name)) {
1853 /* Name mismatch */
1854 6 goto no_match;
1855 }
1856 }
1857
1858 81 return (key);
1859 }
1860
1861 1 return (nullptr);
1862 }
1863
1864 /** Find an index whose first fields are the columns in the array
1865 in the same order and is not marked for deletion
1866 @return matching index, NULL if not found */
1867 139 [[nodiscard]] static dict_index_t *innobase_find_fk_index(
1868 dict_table_t *table, /*!< in: table */
1869 const char **col_names,
1870 /*!< in: column names, or NULL
1871 to use table->col_names */
1872 dict_index_t **drop_index,
1873 /*!< in: indexes to be dropped */
1874 ulint n_drop_index,
1875 /*!< in: size of drop_index[] */
1876 const char **columns, /*!< in: array of column names */
1877 ulint n_cols) /*!< in: number of columns */
1878 {
1879 dict_index_t *index;
1880
1881 139 index = table->first_index();
1882
1883
2/2
✓ Branch 0 taken 296 times.
✓ Branch 1 taken 58 times.
354 while (index != nullptr) {
1884
4/4
✓ Branch 0 taken 293 times.
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 84 times.
✓ Branch 3 taken 212 times.
589 if (!(index->type & DICT_FTS) &&
1885
2/2
✓ Branch 0 taken 84 times.
✓ Branch 1 taken 209 times.
293 dict_foreign_qualify_index(table, col_names, columns, n_cols, index,
1886 nullptr, true, 0)) {
1887
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 81 times.
85 for (ulint i = 0; i < n_drop_index; i++) {
1888
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 1 times.
4 if (index == drop_index[i]) {
1889 /* Skip to-be-dropped indexes. */
1890 3 goto next_rec;
1891 }
1892 }
1893
1894 81 return (index);
1895 }
1896
1897 212 next_rec:
1898 215 index = index->next();
1899 }
1900
1901 58 return (nullptr);
1902 }
1903
1904 /** Check whether given column is a base of stored column.
1905 @param[in] col_name column name
1906 @param[in] table table
1907 @param[in] s_cols list of stored columns
1908 @return true if the given column is a base of stored column,else false. */
1909 12 static bool innobase_col_check_fk(const char *col_name,
1910 const dict_table_t *table,
1911 dict_s_col_list *s_cols) {
1912 12 dict_s_col_list::const_iterator it;
1913
1914
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 9 times.
21 for (it = s_cols->begin(); it != s_cols->end(); ++it) {
1915 12 dict_s_col_t s_col = *it;
1916
1917
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 9 times.
12 for (ulint j = 0; j < s_col.num_base; j++) {
1918
2/4
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
3 if (strcmp(col_name, table->get_col_name(s_col.base_col[j]->ind)) == 0) {
1919 3 return (true);
1920 }
1921 }
1922 }
1923
1924 9 return (false);
1925 }
1926
1927 /** Check whether the foreign key constraint is on base of any stored columns.
1928 @param[in] foreign Foriegn key constraing information
1929 @param[in] table table to which the foreign key objects
1930 to be added
1931 @param[in] s_cols list of stored column information in the table.
1932 @return true if yes, otherwise false. */
1933 136 static bool innobase_check_fk_stored(const dict_foreign_t *foreign,
1934 const dict_table_t *table,
1935 dict_s_col_list *s_cols) {
1936 136 ulint type = foreign->type;
1937
1938 136 type &=
1939 ~(DICT_FOREIGN_ON_DELETE_NO_ACTION | DICT_FOREIGN_ON_UPDATE_NO_ACTION);
1940
1941
4/4
✓ Branch 0 taken 79 times.
✓ Branch 1 taken 57 times.
✓ Branch 2 taken 67 times.
✓ Branch 3 taken 12 times.
136 if (type == 0 || s_cols == nullptr) {
1942 124 return (false);
1943 }
1944
1945
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 9 times.
21 for (ulint i = 0; i < foreign->n_fields; i++) {
1946
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 9 times.
12 if (innobase_col_check_fk(foreign->foreign_col_names[i], table, s_cols)) {
1947 3 return (true);
1948 }
1949 }
1950
1951 9 return (false);
1952 }
1953
1954 /** Create InnoDB foreign key structure from MySQL alter_info
1955 @param[in] ha_alter_info alter table info
1956 @param[in] table_share TABLE_SHARE
1957 @param[in] table table object
1958 @param[in] col_names column names, or NULL to use
1959 table->col_names
1960 @param[in] drop_index indexes to be dropped
1961 @param[in] n_drop_index size of drop_index
1962 @param[out] add_fk foreign constraint added
1963 @param[out] n_add_fk number of foreign constraints
1964 added
1965 @param[in] trx user transaction
1966 @param[in] s_cols list of stored column information
1967 @retval true if successful
1968 @retval false on error (will call my_error()) */
1969 130 [[nodiscard]] static bool innobase_get_foreign_key_info(
1970 Alter_inplace_info *ha_alter_info, const TABLE_SHARE *table_share,
1971 dict_table_t *table, const char **col_names, dict_index_t **drop_index,
1972 ulint n_drop_index, dict_foreign_t **add_fk, ulint *n_add_fk,
1973 const trx_t *trx, dict_s_col_list *s_cols) {
1974 const Foreign_key_spec *fk_key;
1975 130 dict_table_t *referenced_table = nullptr;
1976 130 char *referenced_table_name = nullptr;
1977 130 ulint num_fk = 0;
1978 130 Alter_info *alter_info = ha_alter_info->alter_info;
1979 MDL_ticket *mdl;
1980
1981
1/2
✓ Branch 0 taken 130 times.
✗ Branch 1 not taken.
130 DBUG_TRACE;
1982
1983 130 *n_add_fk = 0;
1984
1985
3/4
✓ Branch 0 taken 130 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 496 times.
✓ Branch 3 taken 124 times.
620 for (const Key_spec *key : alter_info->key_list) {
1986
2/2
✓ Branch 0 taken 357 times.
✓ Branch 1 taken 139 times.
496 if (key->type != KEYTYPE_FOREIGN) {
1987 357 continue;
1988 }
1989
1990 const char *column_names[MAX_NUM_FK_COLUMNS];
1991 139 dict_index_t *index = nullptr;
1992 const char *referenced_column_names[MAX_NUM_FK_COLUMNS];
1993 139 dict_index_t *referenced_index = nullptr;
1994 139 ulint num_col = 0;
1995 139 ulint referenced_num_col = 0;
1996 bool correct_option;
1997 139 char *db_namep = nullptr;
1998 139 char *tbl_namep = nullptr;
1999 139 ulint db_name_len = 0;
2000 139 ulint tbl_name_len = 0;
2001 char db_name[MAX_DATABASE_NAME_LEN];
2002 char tbl_name[MAX_TABLE_NAME_LEN];
2003
2004 139 fk_key = down_cast<const Foreign_key_spec *>(key);
2005
2006
1/2
✓ Branch 0 taken 139 times.
✗ Branch 1 not taken.
139 if (fk_key->columns.size() > 0) {
2007 139 size_t i = 0;
2008
2009 /* Get all the foreign key column info for the
2010 current table */
2011
2/2
✓ Branch 0 taken 159 times.
✓ Branch 1 taken 139 times.
298 while (i < fk_key->columns.size()) {
2012
1/2
✓ Branch 0 taken 159 times.
✗ Branch 1 not taken.
159 column_names[i] = fk_key->columns[i]->get_field_name();
2013
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 159 times.
159 ut_ad(i < MAX_NUM_FK_COLUMNS);
2014 159 i++;
2015 }
2016
2017
1/2
✓ Branch 0 taken 139 times.
✗ Branch 1 not taken.
139 index = innobase_find_fk_index(table, col_names, drop_index, n_drop_index,
2018 column_names, i);
2019
2020 /* MySQL would add a index in the creation
2021 list if no such index for foreign table,
2022 so we have to use DBUG_EXECUTE_IF to simulate
2023 the scenario */
2024
3/4
✓ Branch 0 taken 139 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 138 times.
139 DBUG_EXECUTE_IF("innodb_test_no_foreign_idx", index = nullptr;);
2025
2026 /* Check whether there exist such
2027 index in the the index create clause */
2028
4/4
✓ Branch 0 taken 59 times.
✓ Branch 1 taken 80 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 138 times.
198 if (!index &&
2029
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 58 times.
59 !innobase_find_equiv_index(column_names, static_cast<uint>(i),
2030 59 ha_alter_info->key_info_buffer,
2031
1/2
✓ Branch 0 taken 59 times.
✗ Branch 1 not taken.
59 ha_alter_info->index_add_buffer,
2032 ha_alter_info->index_add_count)) {
2033
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 my_error(ER_FK_NO_INDEX_CHILD, MYF(0),
2034 1 fk_key->name.str ? fk_key->name.str : "",
2035
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 table_share->table_name.str);
2036 6 goto err_exit;
2037 }
2038
2039 138 num_col = i;
2040 }
2041
2042
1/2
✓ Branch 0 taken 138 times.
✗ Branch 1 not taken.
138 add_fk[num_fk] = dict_mem_foreign_create();
2043
2044 #ifndef _WIN32
2045
1/2
✓ Branch 0 taken 138 times.
✗ Branch 1 not taken.
138 if (fk_key->ref_db.str) {
2046
1/2
✓ Branch 0 taken 138 times.
✗ Branch 1 not taken.
138 tablename_to_filename(fk_key->ref_db.str, db_name, MAX_DATABASE_NAME_LEN);
2047 138 db_namep = db_name;
2048 138 db_name_len = strlen(db_name);
2049 }
2050
1/2
✓ Branch 0 taken 138 times.
✗ Branch 1 not taken.
138 if (fk_key->ref_table.str) {
2051
1/2
✓ Branch 0 taken 138 times.
✗ Branch 1 not taken.
138 tablename_to_filename(fk_key->ref_table.str, tbl_name,
2052 MAX_TABLE_NAME_LEN);
2053 138 tbl_namep = tbl_name;
2054 138 tbl_name_len = strlen(tbl_name);
2055 }
2056 #else
2057 ut_ad(fk_key->ref_table.str);
2058 tablename_to_filename(fk_key->ref_table.str, tbl_name, MAX_TABLE_NAME_LEN);
2059 innobase_casedn_str(tbl_name);
2060 tbl_name_len = strlen(tbl_name);
2061 tbl_namep = &tbl_name[0];
2062
2063 if (fk_key->ref_db.str != NULL) {
2064 tablename_to_filename(fk_key->ref_db.str, db_name, MAX_DATABASE_NAME_LEN);
2065 innobase_casedn_str(db_name);
2066 db_name_len = strlen(db_name);
2067 db_namep = &db_name[0];
2068 }
2069 #endif
2070
1/2
✓ Branch 0 taken 138 times.
✗ Branch 1 not taken.
138 dict_sys_mutex_enter();
2071
2072 276 referenced_table_name = dd_get_referenced_table(
2073 138 table->name.m_name, db_namep, db_name_len, tbl_namep, tbl_name_len,
2074
1/2
✓ Branch 0 taken 138 times.
✗ Branch 1 not taken.
138 &referenced_table, &mdl, add_fk[num_fk]->heap);
2075
2076 /* Test the case when referenced_table failed to
2077 open, if trx->check_foreigns is not set, we should
2078 still be able to add the foreign key */
2079
6/10
✓ Branch 0 taken 138 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 137 times.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 1 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 1 times.
✗ Branch 9 not taken.
138 DBUG_EXECUTE_IF(
2080 "innodb_test_open_ref_fail", if (referenced_table) {
2081 dd_table_close(referenced_table, current_thd, &mdl, true);
2082 referenced_table = nullptr;
2083 });
2084
2085
3/4
✓ Branch 0 taken 31 times.
✓ Branch 1 taken 107 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 31 times.
138 if (!referenced_table && trx->check_foreigns) {
2086 dict_sys_mutex_exit();
2087 my_error(ER_FK_CANNOT_OPEN_PARENT, MYF(0), tbl_namep);
2088
2089 goto err_exit;
2090 }
2091
2092
1/2
✓ Branch 0 taken 138 times.
✗ Branch 1 not taken.
138 if (fk_key->ref_columns.size() > 0) {
2093 138 size_t i = 0;
2094
2095
2/2
✓ Branch 0 taken 157 times.
✓ Branch 1 taken 138 times.
295 while (i < fk_key->ref_columns.size()) {
2096
1/2
✓ Branch 0 taken 157 times.
✗ Branch 1 not taken.
157 referenced_column_names[i] = fk_key->ref_columns[i]->get_field_name();
2097
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 157 times.
157 ut_ad(i < MAX_NUM_FK_COLUMNS);
2098 157 i++;
2099 }
2100
2101
2/2
✓ Branch 0 taken 107 times.
✓ Branch 1 taken 31 times.
138 if (referenced_table) {
2102
1/2
✓ Branch 0 taken 107 times.
✗ Branch 1 not taken.
107 referenced_index = dict_foreign_find_index(referenced_table, nullptr,
2103 referenced_column_names, i,
2104 index, true, false);
2105
2106
3/4
✓ Branch 0 taken 107 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 106 times.
107 DBUG_EXECUTE_IF("innodb_test_no_reference_idx",
2107 referenced_index = nullptr;);
2108
2109 /* Check whether there exist such
2110 index in the the index create clause */
2111
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 106 times.
107 if (!referenced_index) {
2112
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 dd_table_close(referenced_table, current_thd, &mdl, true);
2113
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 dict_sys_mutex_exit();
2114
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 my_error(ER_FK_NO_INDEX_PARENT, MYF(0),
2115
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 fk_key->name.str ? fk_key->name.str : "", tbl_namep);
2116 1 goto err_exit;
2117 }
2118 } else {
2119
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 31 times.
31 ut_a(!trx->check_foreigns);
2120 }
2121
2122 137 referenced_num_col = i;
2123 } else {
2124 /* Not possible to add a foreign key without a
2125 referenced column */
2126 if (referenced_table) {
2127 dd_table_close(referenced_table, current_thd, &mdl, true);
2128 }
2129 dict_sys_mutex_exit();
2130 my_error(ER_CANNOT_ADD_FOREIGN, MYF(0), tbl_namep);
2131 goto err_exit;
2132 }
2133
2134
2/4
✓ Branch 0 taken 137 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 137 times.
137 if (!innobase_init_foreign(add_fk[num_fk], fk_key->name.str, table, index,
2135 column_names, num_col, referenced_table_name,
2136 referenced_table, referenced_index,
2137 referenced_column_names, referenced_num_col)) {
2138 if (referenced_table) {
2139 dd_table_close(referenced_table, current_thd, &mdl, true);
2140 }
2141 dict_sys_mutex_exit();
2142 my_error(ER_FK_DUP_NAME, MYF(0), add_fk[num_fk]->id);
2143 goto err_exit;
2144 }
2145
2146
2/2
✓ Branch 0 taken 106 times.
✓ Branch 1 taken 31 times.
137 if (referenced_table) {
2147
2/4
✓ Branch 0 taken 106 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 106 times.
✗ Branch 3 not taken.
106 dd_table_close(referenced_table, current_thd, &mdl, true);
2148 }
2149
1/2
✓ Branch 0 taken 137 times.
✗ Branch 1 not taken.
137 dict_sys_mutex_exit();
2150
2151
1/2
✓ Branch 0 taken 137 times.
✗ Branch 1 not taken.
137 correct_option = innobase_set_foreign_key_option(add_fk[num_fk], fk_key);
2152
2153
3/4
✓ Branch 0 taken 137 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 136 times.
137 DBUG_EXECUTE_IF("innodb_test_wrong_fk_option", correct_option = false;);
2154
2155
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 136 times.
137 if (!correct_option) {
2156 1 my_error(ER_FK_INCORRECT_OPTION, MYF(0), table_share->table_name.str,
2157
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 add_fk[num_fk]->id);
2158 1 goto err_exit;
2159 }
2160
2161
3/4
✓ Branch 0 taken 136 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 133 times.
136 if (innobase_check_fk_stored(add_fk[num_fk], table, s_cols)) {
2162
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 my_error(ER_CANNOT_ADD_FOREIGN_BASE_COL_STORED, MYF(0));
2163 3 goto err_exit;
2164 }
2165
2166 133 num_fk++;
2167 }
2168
2169 124 *n_add_fk = num_fk;
2170
2171 124 return true;
2172 6 err_exit:
2173
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 6 times.
12 for (ulint i = 0; i <= num_fk; i++) {
2174
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 1 times.
6 if (add_fk[i]) {
2175
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
5 dict_foreign_free(add_fk[i]);
2176 }
2177 }
2178
2179 6 return false;
2180 130 }
2181
2182 /** Copies an InnoDB column to a MySQL field. This function is
2183 adapted from row_sel_field_store_in_mysql_format(). */
2184 904 static void innobase_col_to_mysql(
2185 const dict_col_t *col, /*!< in: InnoDB column */
2186 const uchar *data, /*!< in: InnoDB column data */
2187 ulint len, /*!< in: length of data, in bytes */
2188 Field *field) /*!< in/out: MySQL field */
2189 {
2190 uchar *ptr;
2191 904 uchar *dest = field->field_ptr();
2192 904 ulint flen = field->pack_length();
2193
2194
5/7
✓ Branch 0 taken 862 times.
✓ Branch 1 taken 8 times.
✓ Branch 2 taken 15 times.
✓ Branch 3 taken 4 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 15 times.
904 switch (col->mtype) {
2195 862 case DATA_INT:
2196
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 862 times.
862 ut_ad(len == flen);
2197
2198 /* Convert integer data from Innobase to little-endian
2199 format, sign bit restored to normal */
2200
2201
2/2
✓ Branch 0 taken 6655 times.
✓ Branch 1 taken 862 times.
7517 for (ptr = dest + len; ptr != dest;) {
2202 6655 *--ptr = *data++;
2203 }
2204
2205
1/2
✓ Branch 0 taken 862 times.
✗ Branch 1 not taken.
862 if (!field->is_flag_set(UNSIGNED_FLAG)) {
2206 862 ((byte *)dest)[len - 1] ^= 0x80;
2207 }
2208
2209 862 break;
2210
2211 8 case DATA_VARCHAR:
2212 case DATA_VARMYSQL:
2213 case DATA_BINARY:
2214 8 field->reset();
2215
2216
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 if (field->type() == MYSQL_TYPE_VARCHAR) {
2217 /* This is a >= 5.0.3 type true VARCHAR. Store the
2218 length of the data to the first byte or the first
2219 two bytes of dest. */
2220
2221 dest =
2222 8 row_mysql_store_true_var_len(dest, len, flen - field->key_length());
2223 }
2224
2225 /* Copy the actual data */
2226 8 memcpy(dest, data, len);
2227 8 break;
2228
2229 15 case DATA_VAR_POINT:
2230 case DATA_GEOMETRY:
2231 case DATA_BLOB:
2232 /* Skip MySQL BLOBs when reporting an erroneous row
2233 during index creation or table rebuild. */
2234 15 field->set_null();
2235 15 break;
2236
2237 #ifdef UNIV_DEBUG
2238 4 case DATA_MYSQL:
2239
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 ut_ad(flen >= len);
2240
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 ut_ad(DATA_MBMAXLEN(col->mbminmaxlen) >= DATA_MBMINLEN(col->mbminmaxlen));
2241 4 memcpy(dest, data, len);
2242 4 break;
2243
2244 default:
2245 case DATA_SYS_CHILD:
2246 case DATA_SYS:
2247 /* These column types should never be shipped to MySQL. */
2248 ut_d(ut_error);
2249 [[fallthrough]];
2250
2251 case DATA_FLOAT:
2252 case DATA_DOUBLE:
2253 case DATA_DECIMAL:
2254 case DATA_POINT:
2255 /* Above are the valid column types for MySQL data. */
2256 ut_ad(flen == len);
2257 [[fallthrough]];
2258 case DATA_FIXBINARY:
2259 case DATA_CHAR:
2260 /* We may have flen > len when there is a shorter
2261 prefix on the CHAR and BINARY column. */
2262
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 15 times.
15 ut_ad(flen >= len);
2263 #else /* UNIV_DEBUG */
2264 default:
2265 #endif /* UNIV_DEBUG */
2266 15 memcpy(dest, data, len);
2267 }
2268 904 }
2269
2270 /** Copies an InnoDB record to table->record[0].
2271 @param[in,out] table Mysql table
2272 @param[in] rec Record
2273 @param[in] index Index
2274 @param[in] offsets rec_get_offsets( rec, index, ...) */
2275 2 void innobase_rec_to_mysql(struct TABLE *table, const rec_t *rec,
2276 const dict_index_t *index, const ulint *offsets) {
2277 2 uint n_fields = table->s->fields;
2278
2279
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 ut_ad(n_fields ==
2280 dict_table_get_n_tot_u_cols(index->table) -
2281 DICT_TF2_FLAG_IS_SET(index->table, DICT_TF2_FTS_HAS_DOC_ID));
2282
2283
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
4 for (uint i = 0; i < n_fields; i++) {
2284 2 Field *field = table->field[i];
2285 ulint ipos;
2286 ulint ilen;
2287 const uchar *ifield;
2288
2289
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 field->reset();
2290
2291
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 ipos = index->get_col_pos(i, true, false, nullptr);
2292
2293
4/8
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 2 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 2 times.
2 if (ipos == ULINT_UNDEFINED || rec_offs_nth_extern(index, offsets, ipos)) {
2294 null_field:
2295 field->set_null();
2296 continue;
2297 }
2298
2299
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 ifield = rec_get_nth_field_instant(rec, offsets, ipos, index, &ilen);
2300
2301 /* Assign the NULL flag */
2302
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (ilen == UNIV_SQL_NULL) {
2303 ut_ad(field->is_nullable());
2304 goto null_field;
2305 }
2306
2307
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 field->set_notnull();
2308
2309
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
2 innobase_col_to_mysql(index->get_field(ipos)->col, ifield, ilen, field);
2310 }
2311 2 }
2312
2313 /** Copies an InnoDB index entry to table->record[0].
2314 @param[in,out] table Mysql table
2315 @param[in] index Innodb index
2316 @param[in] fields Innodb index fields */
2317 444 void innobase_fields_to_mysql(struct TABLE *table, const dict_index_t *index,
2318 const dfield_t *fields) {
2319 444 uint n_fields = table->s->fields;
2320 444 ulint num_v = 0;
2321
2322
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 444 times.
444 ut_ad(n_fields ==
2323 index->table->get_n_user_cols() +
2324 dict_table_get_n_v_cols(index->table) -
2325 DICT_TF2_FLAG_IS_SET(index->table, DICT_TF2_FTS_HAS_DOC_ID));
2326
2327
2/2
✓ Branch 0 taken 922 times.
✓ Branch 1 taken 443 times.
1365 for (uint i = 0; i < n_fields; i++) {
2328 922 Field *field = table->field[i];
2329 ulint ipos;
2330 ulint col_n;
2331
2332 922 field->reset();
2333
2334
3/4
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 921 times.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
922 if (innobase_is_v_fld(field)) {
2335 1 col_n = num_v;
2336 1 num_v++;
2337 } else {
2338 921 col_n = i - num_v;
2339 }
2340
2341
3/4
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 921 times.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
922 ipos = index->get_col_pos(col_n, true, innobase_is_v_fld(field), nullptr);
2342
2343
6/6
✓ Branch 0 taken 901 times.
✓ Branch 1 taken 21 times.
✓ Branch 2 taken 895 times.
✓ Branch 3 taken 6 times.
✓ Branch 4 taken 27 times.
✓ Branch 5 taken 895 times.
1817 if (ipos == ULINT_UNDEFINED || dfield_is_ext(&fields[ipos]) ||
2344
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 895 times.
895 dfield_is_null(&fields[ipos])) {
2345 27 field->set_null();
2346 } else {
2347 895 field->set_notnull();
2348
2349 895 const dfield_t *df = &fields[ipos];
2350
2351 894 innobase_col_to_mysql(index->get_field(ipos)->col,
2352 894 static_cast<const uchar *>(dfield_get_data(df)),
2353 895 dfield_get_len(df), field);
2354 }
2355 }
2356 443 }
2357
2358 /** Copies an InnoDB row to table->record[0].
2359 @param[in,out] table Mysql table
2360 @param[in] itab Innodb table
2361 @param[in] row Innodb row */
2362 5 void innobase_row_to_mysql(struct TABLE *table, const dict_table_t *itab,
2363 const dtuple_t *row) {
2364 5 uint n_fields = table->s->fields;
2365 5 ulint num_v = 0;
2366
2367 /* The InnoDB row may contain an extra FTS_DOC_ID column at the end. */
2368
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
5 ut_ad(row->n_fields == itab->get_n_cols());
2369
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
5 ut_ad(n_fields == row->n_fields - DATA_N_SYS_COLS +
2370 dict_table_get_n_v_cols(itab) -
2371 DICT_TF2_FLAG_IS_SET(itab, DICT_TF2_FTS_HAS_DOC_ID));
2372
2373
2/2
✓ Branch 0 taken 13 times.
✓ Branch 1 taken 5 times.
18 for (uint i = 0; i < n_fields; i++) {
2374 13 Field *field = table->field[i];
2375
2376 13 field->reset();
2377
2378
3/4
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 11 times.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
13 if (innobase_is_v_fld(field)) {
2379 /* Virtual column are not stored in InnoDB table, so
2380 skip it */
2381 2 num_v++;
2382 2 continue;
2383 }
2384
2385 11 const dfield_t *df = dtuple_get_nth_field(row, i - num_v);
2386
2387
5/6
✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 8 times.
✓ Branch 4 taken 3 times.
✓ Branch 5 taken 8 times.
11 if (dfield_is_ext(df) || dfield_is_null(df)) {
2388 3 field->set_null();
2389 } else {
2390 8 field->set_notnull();
2391
2392 8 innobase_col_to_mysql(itab->get_col(i - num_v),
2393 8 static_cast<const uchar *>(dfield_get_data(df)),
2394 8 dfield_get_len(df), field);
2395 }
2396 }
2397 5 }
2398
2399 /** Resets table->record[0]. */
2400 43912 void innobase_rec_reset(TABLE *table) /*!< in/out: MySQL table */
2401 {
2402 43912 uint n_fields = table->s->fields;
2403 uint i;
2404
2405
2/2
✓ Branch 0 taken 302959 times.
✓ Branch 1 taken 43912 times.
346871 for (i = 0; i < n_fields; i++) {
2406
2/2
✓ Branch 0 taken 302933 times.
✓ Branch 1 taken 26 times.
302959 if (!table->field[i]->m_default_val_expr) {
2407 302933 table->field[i]->set_default();
2408 } else {
2409 26 table->field[i]->copy_data(table->default_values_offset());
2410 }
2411 }
2412 43912 }
2413
2414 /** This function checks that index keys are sensible.
2415 @return 0 or error number */
2416 60002 [[nodiscard]] static int innobase_check_index_keys(
2417 const Alter_inplace_info *info,
2418 /*!< in: indexes to be created or dropped */
2419 const dict_table_t *innodb_table)
2420 /*!< in: Existing indexes */
2421 {
2422
2/2
✓ Branch 0 taken 10655 times.
✓ Branch 1 taken 60002 times.
70657 for (uint key_num = 0; key_num < info->index_add_count; key_num++) {
2423 10655 const KEY &key = info->key_info_buffer[info->index_add_buffer[key_num]];
2424
2425 /* Check that the same index name does not appear
2426 twice in indexes to be created. */
2427
2428
2/2
✓ Branch 0 taken 2202 times.
✓ Branch 1 taken 10655 times.
12857 for (ulint i = 0; i < key_num; i++) {
2429 2202 const KEY &key2 = info->key_info_buffer[info->index_add_buffer[i]];
2430
2431
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2202 times.
2202 if (0 == strcmp(key.name, key2.name)) {
2432 my_error(ER_WRONG_NAME_FOR_INDEX, MYF(0), key.name);
2433
2434 return (ER_WRONG_NAME_FOR_INDEX);
2435 }
2436 }
2437
2438 /* Check that the same index name does not already exist. */
2439
2440 const dict_index_t *index;
2441
2442
2/2
✓ Branch 0 taken 20829 times.
✓ Branch 1 taken 9912 times.
30741 for (index = innodb_table->first_index(); index; index = index->next()) {
2443
6/6
✓ Branch 0 taken 20827 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 743 times.
✓ Branch 3 taken 20084 times.
✓ Branch 4 taken 743 times.
✓ Branch 5 taken 20086 times.
20829 if (index->is_committed() && !strcmp(key.name, index->name)) {
2444 743 break;
2445 }
2446 }
2447
2448 /* Now we are in a situation where we have "ADD INDEX x"
2449 and an index by the same name already exists. We have 4
2450 possible cases:
2451 1. No further clauses for an index x are given. Should reject
2452 the operation.
2453 2. "DROP INDEX x" is given. Should allow the operation.
2454 3. "RENAME INDEX x TO y" is given. Should allow the operation.
2455 4. "DROP INDEX x, RENAME INDEX x TO y" is given. Should allow
2456 the operation, since no name clash occurs. In this particular
2457 case MySQL cancels the operation without calling InnoDB
2458 methods. */
2459
2460
2/2
✓ Branch 0 taken 743 times.
✓ Branch 1 taken 9912 times.
10655 if (index) {
2461 /* If a key by the same name is being created and
2462 dropped, the name clash is OK. */
2463
2/2
✓ Branch 0 taken 764 times.
✓ Branch 1 taken 12 times.
776 for (uint i = 0; i < info->index_drop_count; i++) {
2464 764 const KEY *drop_key = info->index_drop_buffer[i];
2465
2466
2/2
✓ Branch 0 taken 731 times.
✓ Branch 1 taken 33 times.
764 if (0 == strcmp(key.name, drop_key->name)) {
2467 731 goto name_ok;
2468 }
2469 }
2470
2471 /* If a key by the same name is being created and
2472 renamed, the name clash is OK. E.g.
2473 ALTER TABLE t ADD INDEX i (col), RENAME INDEX i TO x
2474 where the index "i" exists prior to the ALTER command.
2475 In this case we:
2476 1. rename the existing index from "i" to "x"
2477 2. add the new index "i" */
2478
1/2
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
12 for (uint i = 0; i < info->index_rename_count; i++) {
2479 12 const KEY_PAIR *pair = &info->index_rename_buffer[i];
2480
2481
1/2
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
12 if (0 == strcmp(key.name, pair->old_key->name)) {
2482 12 goto name_ok;
2483 }
2484 }
2485
2486 my_error(ER_WRONG_NAME_FOR_INDEX, MYF(0), key.name);
2487
2488 return (ER_WRONG_NAME_FOR_INDEX);
2489 }
2490
2491 9912 name_ok:
2492
2/2
✓ Branch 0 taken 14766 times.
✓ Branch 1 taken 10655 times.
25421 for (ulint i = 0; i < key.user_defined_key_parts; i++) {
2493 14766 const KEY_PART_INFO &key_part1 = key.key_part[i];
2494 14766 const Field *field = key_part1.field;
2495 ulint is_unsigned;
2496
2497
3/4
✓ Branch 0 taken 14766 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5450 times.
✓ Branch 3 taken 9316 times.
14766 switch (get_innobase_type_from_mysql_type(&is_unsigned, field)) {
2498 5450 default:
2499 5450 break;
2500 9316 case DATA_INT:
2501 case DATA_FLOAT:
2502 case DATA_DOUBLE:
2503 case DATA_DECIMAL:
2504 /* Check that MySQL does not try to
2505 create a column prefix index field on
2506 an inappropriate data type. */
2507
2508
2/4
✓ Branch 0 taken 9316 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 9316 times.
9316 if (field->type() == MYSQL_TYPE_VARCHAR) {
2509 if (key_part1.length >=
2510 field->pack_length() - field->get_length_bytes()) {
2511 break;
2512 }
2513 } else {
2514
4/6
✓ Branch 0 taken 9316 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 46 times.
✓ Branch 3 taken 9270 times.
✓ Branch 4 taken 9316 times.
✗ Branch 5 not taken.
9362 if (key_part1.length >= field->pack_length() ||
2515
2/4
✓ Branch 0 taken 46 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 46 times.
✗ Branch 3 not taken.
46 innobase_is_multi_value_fld(field)) {
2516 9316 break;
2517 }
2518 }
2519
2520 my_error(ER_WRONG_KEY_COLUMN, MYF(0), field->field_name);
2521 return (ER_WRONG_KEY_COLUMN);
2522 }
2523
2524 /* Check that the same column does not appear
2525 twice in the index. */
2526
2527
2/2
✓ Branch 0 taken 13663 times.
✓ Branch 1 taken 14766 times.
28429 for (ulint j = 0; j < i; j++) {
2528 13663 const KEY_PART_INFO &key_part2 = key.key_part[j];
2529
2530
1/2
✓ Branch 0 taken 13663 times.
✗ Branch 1 not taken.
13663 if (key_part1.fieldnr != key_part2.fieldnr) {
2531 13663 continue;
2532 }
2533
2534 my_error(ER_WRONG_KEY_COLUMN, MYF(0), field->field_name);
2535 return (ER_WRONG_KEY_COLUMN);
2536 }
2537 }
2538 }
2539
2540 60002 return (0);
2541 }
2542
2543 /** Create index field definition for key part
2544 @param[in] altered_table MySQL table that is being altered,
2545 or NULL if a new clustered index
2546 is not being created
2547 @param[in] key_part MySQL key definition
2548 @param[in,out] index_field index field
2549 @param[in] new_clustered new cluster */
2550 71289 static void innobase_create_index_field_def(const TABLE *altered_table,
2551 const KEY_PART_INFO *key_part,
2552 ddl::Index_field *index_field,
2553 bool new_clustered) {
2554 const Field *field;
2555 ulint is_unsigned;
2556 ulint col_type;
2557 71289 ulint num_v = 0;
2558
2559
1/2
✓ Branch 0 taken 71289 times.
✗ Branch 1 not taken.
71289 DBUG_TRACE;
2560
2561
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 71289 times.
71289 ut_ad(key_part);
2562
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 71289 times.
71289 ut_ad(index_field);
2563
2564
2/2
✓ Branch 0 taken 61057 times.
✓ Branch 1 taken 10232 times.
71289 field =
2565 61057 new_clustered ? altered_table->field[key_part->fieldnr] : key_part->field;
2566
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 71289 times.
71289 ut_a(field);
2567
2568
2/2
✓ Branch 0 taken 121533 times.
✓ Branch 1 taken 71289 times.
192822 for (ulint i = 0; i < key_part->fieldnr; i++) {
2569
4/4
✓ Branch 0 taken 379 times.
✓ Branch 1 taken 121154 times.
✓ Branch 2 taken 278 times.
✓ Branch 3 taken 101 times.
121533 if (innobase_is_v_fld(altered_table->field[i])) {
2570 278 num_v++;
2571 }
2572 }
2573
2574
1/2
✓ Branch 0 taken 71289 times.
✗ Branch 1 not taken.
71289 col_type = get_innobase_type_from_mysql_type(&is_unsigned, field);
2575
2576
1/2
✓ Branch 0 taken 71289 times.
✗ Branch 1 not taken.
71289 index_field->m_is_multi_value = innobase_is_multi_value_fld(field);
2577
3/4
✓ Branch 0 taken 340 times.
✓ Branch 1 taken 70949 times.
✓ Branch 2 taken 340 times.
✗ Branch 3 not taken.
71289 if (!field->stored_in_db && field->gcol_info) {
2578 340 index_field->m_is_v_col = true;
2579 340 index_field->m_col_no = num_v;
2580 } else {
2581 70949 index_field->m_is_v_col = false;
2582 70949 index_field->m_col_no = key_part->fieldnr - num_v;
2583 }
2584 71289 index_field->m_is_ascending = !(key_part->key_part_flag & HA_REVERSE_SORT);
2585
2586 /* No prefix index on multi-value field */
2587
6/6
✓ Branch 0 taken 71191 times.
✓ Branch 1 taken 98 times.
✓ Branch 2 taken 70551 times.
✓ Branch 3 taken 640 times.
✓ Branch 4 taken 989 times.
✓ Branch 5 taken 70300 times.
213031 if (!index_field->m_is_multi_value &&
2588 71191 (DATA_LARGE_MTYPE(col_type) ||
2589
3/4
✓ Branch 0 taken 70551 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5989 times.
✓ Branch 3 taken 64562 times.
70551 (key_part->length < field->pack_length() &&
2590
3/4
✓ Branch 0 taken 5989 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5907 times.
✓ Branch 3 taken 82 times.
5989 field->type() != MYSQL_TYPE_VARCHAR) ||
2591
3/4
✓ Branch 0 taken 70469 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5907 times.
✓ Branch 3 taken 64562 times.
70469 (field->type() == MYSQL_TYPE_VARCHAR &&
2592
4/6
✓ Branch 0 taken 5907 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5907 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 267 times.
✓ Branch 5 taken 5640 times.
5907 key_part->length < field->pack_length() - field->get_length_bytes()))) {
2593 989 index_field->m_prefix_len = key_part->length;
2594 } else {
2595 70300 index_field->m_prefix_len = 0;
2596 }
2597 71289 }
2598
2599 template <typename Index>
2600 const dd::Index *get_dd_index(const Index *index);
2601
2602 template <>
2603 135 const dd::Index *get_dd_index<dd::Index>(const dd::Index *dd_index) {
2604 135 return dd_index;
2605 }
2606
2607 template <>
2608 const dd::Index *get_dd_index<dd::Partition_index>(
2609 const dd::Partition_index *dd_index) {
2610 return (dd_index != nullptr) ? &dd_index->index() : nullptr;
2611 }
2612
2613 /** Create index definition for key
2614 @param[in] altered_table MySQL table that is being altered
2615 @param[in] new_dd_tab New dd table
2616 @param[in] keys Key definitions
2617 @param[in] key_number MySQL key number
2618 @param[in] new_clustered true if generating a new clustered index
2619 on the table
2620 @param[in] key_clustered true if this is the new clustered index
2621 @param[out] index_def Index definition
2622 @param[in] heap heap where memory is allocated */
2623 template <typename Table>
2624 90912 static void innobase_create_index_def(const TABLE *altered_table,
2625 const Table *new_dd_tab, const KEY *keys,
2626 ulint key_number, bool new_clustered,
2627 bool key_clustered,
2628 ddl::Index_defn *index_def,
2629 mem_heap_t *heap) {
2630 ulint i;
2631 90912 const KEY *key = &keys[key_number];
2632 90912 ulint n_fields = key->user_defined_key_parts;
2633
2634
1/2
✓ Branch 0 taken 45456 times.
✗ Branch 1 not taken.
90912 DBUG_TRACE;
2635
3/4
✓ Branch 0 taken 26076 times.
✓ Branch 1 taken 19380 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 26076 times.
90912 assert(!key_clustered || new_clustered);
2636
2637 90912 index_def->m_fields = static_cast<ddl::Index_field *>(
2638
1/2
✓ Branch 0 taken 45456 times.
✗ Branch 1 not taken.
90912 mem_heap_alloc(heap, n_fields * sizeof *index_def->m_fields));
2639
2640 90912 index_def->m_parser = nullptr;
2641 90912 index_def->m_is_ngram = false;
2642 90912 index_def->m_key_number = key_number;
2643 90912 index_def->m_n_fields = n_fields;
2644
1/2
✓ Branch 0 taken 45456 times.
✗ Branch 1 not taken.
90912 index_def->m_name = mem_heap_strdup(heap, key->name);
2645 90912 index_def->m_rebuild = new_clustered;
2646
2647 /* If this is a spatial index, we need to fetch the SRID */
2648
2/2
✓ Branch 0 taken 135 times.
✓ Branch 1 taken 45321 times.
90912 if (key->flags & HA_SPATIAL) {
2649 270 ulint dd_key_num =
2650
2/2
✓ Branch 0 taken 71 times.
✓ Branch 1 taken 64 times.
270 key_number + ((altered_table->s->primary_key == MAX_KEY) ? 1 : 0);
2651
2652 270 const auto *dd_index_auto =
2653 270 (index_def->m_key_number != ULINT_UNDEFINED)
2654
3/6
✓ Branch 0 taken 135 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 135 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 135 times.
✗ Branch 5 not taken.
270 ? const_cast<const Table *>(new_dd_tab)->indexes()[dd_key_num]
2655 : nullptr;
2656
2657
1/2
✓ Branch 0 taken 135 times.
✗ Branch 1 not taken.
270 const dd::Index *dd_index = get_dd_index(dd_index_auto);
2658
2659
1/2
✓ Branch 0 taken 135 times.
✗ Branch 1 not taken.
270 if (dd_index != nullptr) {
2660
2/4
✓ Branch 0 taken 135 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 135 times.
270 ut_ad(dd_index->name() == key->name);
2661 /* Spatial index indexes on only one column */
2662 size_t geom_col_idx;
2663
2/4
✓ Branch 0 taken 135 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 135 times.
✗ Branch 3 not taken.
270 for (geom_col_idx = 0; geom_col_idx < dd_index->elements().size();
2664 ++geom_col_idx) {
2665
5/10
✓ Branch 0 taken 135 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 135 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 135 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 135 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 135 times.
✗ Branch 9 not taken.
270 if (!dd_index->elements()[geom_col_idx]->column().is_se_hidden()) break;
2666 }
2667
3/6
✓ Branch 0 taken 135 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 135 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 135 times.
✗ Branch 5 not taken.
270 const dd::Column &col = dd_index->elements()[geom_col_idx]->column();
2668
1/2
✓ Branch 0 taken 135 times.
✗ Branch 1 not taken.
270 bool has_value = col.srs_id().has_value();
2669 270 index_def->m_srid_is_valid = has_value;
2670
4/6
✓ Branch 0 taken 118 times.
✓ Branch 1 taken 17 times.
✓ Branch 2 taken 118 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 118 times.
✗ Branch 5 not taken.
270 index_def->m_srid = has_value ? col.srs_id().value() : 0;
2671 }
2672 }
2673
2674
2/2
✓ Branch 0 taken 26076 times.
✓ Branch 1 taken 19380 times.
90912 if (key_clustered) {
2675
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 26076 times.
52152 assert(!(key->flags & (HA_FULLTEXT | HA_SPATIAL)));
2676
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 26076 times.
52152 assert(key->flags & HA_NOSAME);
2677 52152 index_def->m_ind_type = DICT_CLUSTERED | DICT_UNIQUE;
2678
2/2
✓ Branch 0 taken 439 times.
✓ Branch 1 taken 18941 times.
38760 } else if (key->flags & HA_FULLTEXT) {
2679
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 439 times.
878 assert(!(key->flags & (HA_SPATIAL | HA_NOSAME)));
2680
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 439 times.
878 assert(!(key->flags & HA_KEYFLAG_MASK &
2681 ~(HA_FULLTEXT | HA_PACK_KEY | HA_BINARY_PACK_KEY)));
2682 878 index_def->m_ind_type = DICT_FTS;
2683
2684 /* Set plugin parser */
2685 /* Note: key->parser is only parser name,
2686 we need to get parser from altered_table instead */
2687
2/2
✓ Branch 0 taken 239 times.
✓ Branch 1 taken 200 times.
878 if (key->flags & HA_USES_PARSER) {
2688
1/2
✓ Branch 0 taken 1152 times.
✗ Branch 1 not taken.
2304 for (ulint j = 0; j < altered_table->s->keys; j++) {
2689
2/2
✓ Branch 0 taken 239 times.
✓ Branch 1 taken 913 times.
2304 if (ut_strcmp(altered_table->key_info[j].name, key->name) == 0) {
2690
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 239 times.
478 ut_ad(altered_table->key_info[j].flags & HA_USES_PARSER);
2691
2692 478 plugin_ref parser = altered_table->key_info[j].parser;
2693 478 index_def->m_parser =
2694 478 static_cast<st_mysql_ftparser *>(plugin_decl(parser)->info);
2695
2696 478 index_def->m_is_ngram =
2697 478 strncmp(plugin_name(parser)->str, FTS_NGRAM_PARSER_NAME,
2698 478 plugin_name(parser)->length) == 0;
2699
2700 478 break;
2701 }
2702 }
2703
2704
3/4
✓ Branch 0 taken 239 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 238 times.
478 DBUG_EXECUTE_IF("fts_instrument_use_default_parser",
2705 index_def->m_parser = &fts_default_parser;);
2706
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 239 times.
478 ut_ad(index_def->m_parser);
2707 }
2708
2/2
✓ Branch 0 taken 135 times.
✓ Branch 1 taken 18806 times.
37882 } else if (key->flags & HA_SPATIAL) {
2709
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 135 times.
270 assert(!(key->flags & HA_NOSAME));
2710 270 index_def->m_ind_type = DICT_SPATIAL;
2711
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 135 times.
270 ut_ad(n_fields == 1);
2712 270 ulint num_v = 0;
2713
2714 /* Need to count the virtual fields before this spatial
2715 indexed field */
2716
2/2
✓ Branch 0 taken 168 times.
✓ Branch 1 taken 135 times.
606 for (ulint i = 0; i < key->key_part->fieldnr; i++) {
2717
4/4
✓ Branch 0 taken 17 times.
✓ Branch 1 taken 151 times.
✓ Branch 2 taken 14 times.
✓ Branch 3 taken 3 times.
336 if (innobase_is_v_fld(altered_table->field[i])) {
2718 28 num_v++;
2719 }
2720 }
2721 270 index_def->m_fields[0].m_col_no = key->key_part[0].fieldnr - num_v;
2722 270 index_def->m_fields[0].m_prefix_len = 0;
2723 270 index_def->m_fields[0].m_is_v_col = false;
2724
2725 /* Currently only ascending order is supported in spatial
2726 index. */
2727
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 135 times.
270 ut_ad(!(key->key_part[0].key_part_flag & HA_REVERSE_SORT));
2728 270 index_def->m_fields[0].m_is_ascending = true;
2729
2730
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 135 times.
270 if (!key->key_part[0].field->stored_in_db &&
2731 key->key_part[0].field->gcol_info) {
2732 index_def->m_fields[0].m_is_v_col = true;
2733 /* Currently, the spatial index cannot be created
2734 on virtual columns. It is blocked in server
2735 layer */
2736 ut_d(ut_error);
2737 } else {
2738 270 index_def->m_fields[0].m_is_v_col = false;
2739 }
2740 } else {
2741 37612 index_def->m_ind_type = (key->flags & HA_NOSAME) ? DICT_UNIQUE : 0;
2742 }
2743
2744
2/2
✓ Branch 0 taken 45321 times.
✓ Branch 1 taken 135 times.
90912 if (!(key->flags & HA_SPATIAL)) {
2745
2/2
✓ Branch 0 taken 71289 times.
✓ Branch 1 taken 45321 times.
233220 for (i = 0; i < n_fields; i++) {
2746 142578 innobase_create_index_field_def(altered_table, &key->key_part[i],
2747
1/2
✓ Branch 0 taken 71289 times.
✗ Branch 1 not taken.
142578 &index_def->m_fields[i], new_clustered);
2748
2749
2/2
✓ Branch 0 taken 340 times.
✓ Branch 1 taken 70949 times.
142578 if (index_def->m_fields[i].m_is_v_col) {
2750 680 index_def->m_ind_type |= DICT_VIRTUAL;
2751 }
2752
2753
2/2
✓ Branch 0 taken 98 times.
✓ Branch 1 taken 71191 times.
142578 if (index_def->m_fields[i].m_is_multi_value) {
2754 196 index_def->m_ind_type |= DICT_MULTI_VALUE;
2755 }
2756 }
2757 }
2758 90912 }
2759
2760 /** Check whether the table has the FTS_DOC_ID column
2761 @return whether there exists an FTS_DOC_ID column */
2762 463 bool innobase_fts_check_doc_id_col(
2763 const dict_table_t *table, /*!< in: InnoDB table with
2764 fulltext index */
2765 const TABLE *altered_table,
2766 /*!< in: MySQL table with
2767 fulltext index */
2768 ulint *fts_doc_col_no,
2769 /*!< out: The column number for
2770 Doc ID, or ULINT_UNDEFINED
2771 if it is of wrong type */
2772 ulint *num_v) /*!< out: number of virtual column */
2773 {
2774 463 *fts_doc_col_no = ULINT_UNDEFINED;
2775
2776 463 const uint n_cols = altered_table->s->fields;
2777 ulint i;
2778
2779 463 *num_v = 0;
2780
2781
2/2
✓ Branch 0 taken 2829 times.
✓ Branch 1 taken 426 times.
3255 for (i = 0; i < n_cols; i++) {
2782 2829 const Field *field = altered_table->field[i];
2783
2784
4/4
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 2823 times.
✓ Branch 2 taken 4 times.
✓ Branch 3 taken 2 times.
2829 if (innobase_is_v_fld(field)) {
2785 4 (*num_v)++;
2786 }
2787
2788
2/2
✓ Branch 0 taken 2792 times.
✓ Branch 1 taken 37 times.
2829 if (my_strcasecmp(system_charset_info, field->field_name,
2789 FTS_DOC_ID_COL_NAME)) {
2790 2792 continue;
2791 }
2792
2793
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 37 times.
37 if (strcmp(field->field_name, FTS_DOC_ID_COL_NAME)) {
2794 my_error(ER_WRONG_COLUMN_NAME, MYF(0), field->field_name);
2795 37 } else if (field->type() != MYSQL_TYPE_LONGLONG ||
2796
2/4
✓ Branch 0 taken 37 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 37 times.
✗ Branch 3 not taken.
37 field->pack_length() != 8 || field->is_nullable() ||
2797
6/10
✓ Branch 0 taken 37 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 36 times.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 36 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 1 times.
✓ Branch 9 taken 36 times.
74 !field->is_flag_set(UNSIGNED_FLAG) || innobase_is_v_fld(field)) {
2798 1 my_error(ER_INNODB_FT_WRONG_DOCID_COLUMN, MYF(0), field->field_name);
2799 } else {
2800 36 *fts_doc_col_no = i - *num_v;
2801 }
2802
2803 37 return (true);
2804 }
2805
2806
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 426 times.
426 if (!table) {
2807 return (false);
2808 }
2809
2810 /* Not to count the virtual columns */
2811 426 i -= *num_v;
2812
2813
2/2
✓ Branch 0 taken 300 times.
✓ Branch 1 taken 126 times.
426 for (; i + DATA_N_SYS_COLS < (uint)table->n_cols; i++) {
2814 300 const char *name = table->get_col_name(i);
2815
2816
1/2
✓ Branch 0 taken 300 times.
✗ Branch 1 not taken.
300 if (strcmp(name, FTS_DOC_ID_COL_NAME) == 0) {
2817 #ifdef UNIV_DEBUG
2818 const dict_col_t *col;
2819
2820 300 col = table->get_col(i);
2821
2822 /* Because the FTS_DOC_ID does not exist in
2823 the MySQL data dictionary, this must be the
2824 internally created FTS_DOC_ID column. */
2825
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 300 times.
300 ut_ad(col->mtype == DATA_INT);
2826
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 300 times.
300 ut_ad(col->len == 8);
2827
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 300 times.
300 ut_ad(col->prtype & DATA_NOT_NULL);
2828
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 300 times.
300 ut_ad(col->prtype & DATA_UNSIGNED);
2829 #endif /* UNIV_DEBUG */
2830 300 *fts_doc_col_no = i;
2831 300 return (true);
2832 }
2833 }
2834
2835 126 return (false);
2836 }
2837
2838 /** Check whether the table has a unique index with FTS_DOC_ID_INDEX_NAME
2839 on the Doc ID column.
2840 @return the status of the FTS_DOC_ID index */
2841 6311 enum fts_doc_id_index_enum innobase_fts_check_doc_id_index(
2842 const dict_table_t *table, /*!< in: table definition */
2843 const TABLE *altered_table, /*!< in: MySQL table
2844 that is being altered */
2845 ulint *fts_doc_col_no) /*!< out: The column number for
2846 Doc ID, or ULINT_UNDEFINED
2847 if it is being created in
2848 ha_alter_info */
2849 {
2850 const dict_index_t *index;
2851 const dict_field_t *field;
2852
2853
2/2
✓ Branch 0 taken 461 times.
✓ Branch 1 taken 5850 times.
6311 if (altered_table) {
2854 /* Check if a unique index with the name of
2855 FTS_DOC_ID_INDEX_NAME is being created. */
2856
2857
2/2
✓ Branch 0 taken 1707 times.
✓ Branch 1 taken 445 times.
2152 for (uint i = 0; i < altered_table->s->keys; i++) {
2858 1707 const KEY &key = altered_table->key_info[i];
2859
2860
2/2
✓ Branch 0 taken 1691 times.
✓ Branch 1 taken 16 times.
1707 if (innobase_strcasecmp(key.name, FTS_DOC_ID_INDEX_NAME)) {
2861 1691 continue;
2862 }
2863
2864
1/2
✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
16 if ((key.flags & HA_NOSAME) &&
2865
1/2
✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
16 key.user_defined_key_parts == 1
2866 /* For now, we do not allow a descending index,
2867 because fts_doc_fetch_by_doc_id() uses the
2868 InnoDB SQL interpreter to look up FTS_DOC_ID. */
2869
1/2
✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
16 && !(key.key_part[0].key_part_flag & HA_REVERSE_SORT) &&
2870
1/2
✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
16 !strcmp(key.name, FTS_DOC_ID_INDEX_NAME) &&
2871
1/2
✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
16 !strcmp(key.key_part[0].field->field_name, FTS_DOC_ID_COL_NAME)) {
2872
1/2
✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
16 if (fts_doc_col_no) {
2873 16 *fts_doc_col_no = ULINT_UNDEFINED;
2874 }
2875 16 return (FTS_EXIST_DOC_ID_INDEX);
2876 } else {
2877 return (FTS_INCORRECT_DOC_ID_INDEX);
2878 }
2879 }
2880 }
2881
2882
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6295 times.
6295 if (!table) {
2883 return (FTS_NOT_EXIST_DOC_ID_INDEX);
2884 }
2885
2886
2/2
✓ Branch 0 taken 15618 times.
✓ Branch 1 taken 141 times.
15759 for (index = table->first_index(); index; index = index->next()) {
2887 /* Check if there exists a unique index with the name of
2888 FTS_DOC_ID_INDEX_NAME */
2889
2/2
✓ Branch 0 taken 9464 times.
✓ Branch 1 taken 6154 times.
15618 if (innobase_strcasecmp(index->name, FTS_DOC_ID_INDEX_NAME)) {
2890 9464 continue;
2891 }
2892
2893 6154 if (!dict_index_is_unique(index) ||
2894
1/2
✓ Branch 0 taken 6154 times.
✗ Branch 1 not taken.
6154 dict_index_get_n_unique(index) > 1
2895 /* For now, we do not allow a descending index,
2896 because fts_doc_fetch_by_doc_id() uses the
2897 InnoDB SQL interpreter to look up FTS_DOC_ID. */
2898
3/6
✓ Branch 0 taken 6154 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 6154 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 6154 times.
18462 || !index->get_field(0)->is_ascending ||
2899
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6154 times.
6154 strcmp(index->name, FTS_DOC_ID_INDEX_NAME)) {
2900 return (FTS_INCORRECT_DOC_ID_INDEX);
2901 }
2902
2903 /* Check whether the index has FTS_DOC_ID as its
2904 first column */
2905 6154 field = index->get_field(0);
2906
2907 /* The column would be of a BIGINT data type */
2908 6154 if (strcmp(field->name, FTS_DOC_ID_COL_NAME) == 0 &&
2909
2/4
✓ Branch 0 taken 6154 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 6154 times.
✗ Branch 3 not taken.
6154 field->col->mtype == DATA_INT && field->col->len == 8 &&
2910
4/8
✓ Branch 0 taken 6154 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 6154 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 6154 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 6154 times.
✗ Branch 7 not taken.
12308 field->col->prtype & DATA_NOT_NULL && !field->col->is_virtual()) {
2911
2/2
✓ Branch 0 taken 568 times.
✓ Branch 1 taken 5586 times.
6154 if (fts_doc_col_no) {
2912 568 *fts_doc_col_no = dict_col_get_no(field->col);
2913 }
2914 6154 return (FTS_EXIST_DOC_ID_INDEX);
2915 } else {
2916 return (FTS_INCORRECT_DOC_ID_INDEX);
2917 }
2918 }
2919
2920 /* Not found */
2921 141 return (FTS_NOT_EXIST_DOC_ID_INDEX);
2922 }
2923 /** Check whether the table has a unique index with FTS_DOC_ID_INDEX_NAME
2924 on the Doc ID column in MySQL create index definition.
2925 @return FTS_EXIST_DOC_ID_INDEX if there exists the FTS_DOC_ID index,
2926 FTS_INCORRECT_DOC_ID_INDEX if the FTS_DOC_ID index is of wrong format */
2927 734 enum fts_doc_id_index_enum innobase_fts_check_doc_id_index_in_def(
2928 ulint n_key, /*!< in: Number of keys */
2929 const KEY *key_info) /*!< in: Key definition */
2930 {
2931 /* Check whether there is a "FTS_DOC_ID_INDEX" in the to be built index
2932 list */
2933
2/2
✓ Branch 0 taken 1475 times.
✓ Branch 1 taken 726 times.
2201 for (ulint j = 0; j < n_key; j++) {
2934 1475 const KEY *key = &key_info[j];
2935
2936
2/2
✓ Branch 0 taken 1467 times.
✓ Branch 1 taken 8 times.
1475 if (innobase_strcasecmp(key->name, FTS_DOC_ID_INDEX_NAME)) {
2937 1467 continue;
2938 }
2939
2940 /* Do a check on FTS DOC ID_INDEX, it must be unique,
2941 named as "FTS_DOC_ID_INDEX" and on column "FTS_DOC_ID" */
2942
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 if (!(key->flags & HA_NOSAME) ||
2943
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 key->user_defined_key_parts != 1
2944 /* For now, we do not allow a descending index,
2945 because fts_doc_fetch_by_doc_id() uses the
2946 InnoDB SQL interpreter to look up FTS_DOC_ID. */
2947
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 || (key->key_part[0].key_part_flag & HA_REVERSE_SORT) ||
2948
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 strcmp(key->name, FTS_DOC_ID_INDEX_NAME) ||
2949
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 strcmp(key->key_part[0].field->field_name, FTS_DOC_ID_COL_NAME)) {
2950 return (FTS_INCORRECT_DOC_ID_INDEX);
2951 }
2952
2953 8 return (FTS_EXIST_DOC_ID_INDEX);
2954 }
2955
2956 726 return (FTS_NOT_EXIST_DOC_ID_INDEX);
2957 }
2958
2959 /** Create an index table where indexes are ordered as follows:
2960
2961 IF a new primary key is defined for the table THEN
2962
2963 1) New primary key
2964 2) The remaining keys in key_info
2965
2966 ELSE
2967
2968 1) All new indexes in the order they arrive from MySQL
2969
2970 ENDIF
2971
2972 @return key definitions */
2973 template <typename Table>
2974 [[nodiscard]] static MY_ATTRIBUTE((malloc)) ddl::Index_defn
2975 88204 *innobase_create_key_defs(mem_heap_t *heap,
2976 /*!< in/out: memory heap where space for key
2977 definitions are allocated */
2978 const Alter_inplace_info *ha_alter_info,
2979 /*!< in: alter operation */
2980 const TABLE *altered_table,
2981 /*!< in: MySQL table that is being altered */
2982 const Table *new_dd_table,
2983 /*!< in: new dd table */
2984 ulint &n_add,
2985 /*!< in/out: number of indexes to be created */
2986 ulint &n_fts_add,
2987 /*!< out: number of FTS indexes to be created */
2988 bool got_default_clust,
2989 /*!< in: whether the table lacks a primary key */
2990 ulint &fts_doc_id_col,
2991 /*!< in: The column number for Doc ID */
2992 bool &add_fts_doc_id,
2993 /*!< in: whether we need to add new DOC ID
2994 column for FTS index */
2995 bool &add_fts_doc_idx,
2996 /*!< in: whether we need to add new DOC ID
2997 index for FTS index */
2998 const TABLE *table, bool is_file_per_table)
2999 /*!<in: old_table MySQL table as it is before the ALTER operation */
3000 {
3001 ddl::Index_defn *indexdef;
3002 ddl::Index_defn *index_defs;
3003 bool new_primary;
3004 88204 const uint *const add = ha_alter_info->index_add_buffer;
3005 88204 const KEY *const key_info = ha_alter_info->key_info_buffer;
3006
3007
1/2
✓ Branch 0 taken 44102 times.
✗ Branch 1 not taken.
88204 DBUG_TRACE;
3008
3/4
✓ Branch 0 taken 126 times.
✓ Branch 1 taken 43976 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 126 times.
88204 assert(!add_fts_doc_id || add_fts_doc_idx);
3009
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 44102 times.
88204 assert(ha_alter_info->index_add_count == n_add);
3010
3011 /* If there is a primary key, it is always the first index
3012 defined for the innodb_table. */
3013
3014
5/6
✓ Branch 0 taken 10419 times.
✓ Branch 1 taken 33683 times.
✓ Branch 2 taken 10419 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1261 times.
✓ Branch 5 taken 9158 times.
88204 new_primary = n_add > 0 && !my_strcasecmp(system_charset_info,
3015 key_info[*add].name, "PRIMARY");
3016 88204 n_fts_add = 0;
3017
3018 /* If there is a UNIQUE INDEX consisting entirely of NOT NULL
3019 columns and if the index does not contain column prefix(es)
3020 (only prefix/part of the column is indexed), MySQL will treat the
3021 index as a PRIMARY KEY unless the table already has one. */
3022
3023
4/6
✓ Branch 0 taken 15707 times.
✓ Branch 1 taken 28395 times.
✓ Branch 2 taken 15707 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 44102 times.
88204 ut_ad(altered_table->s->primary_key == 0 ||
3024 altered_table->s->primary_key == MAX_KEY);
3025
3026
4/4
✓ Branch 0 taken 16765 times.
✓ Branch 1 taken 27337 times.
✓ Branch 2 taken 16168 times.
✓ Branch 3 taken 597 times.
88204 if (got_default_clust && !new_primary) {
3027 32336 new_primary = (altered_table->s->primary_key != MAX_KEY);
3028 }
3029
3030 88204 const bool rebuild =
3031
6/6
✓ Branch 0 taken 42380 times.
✓ Branch 1 taken 1722 times.
✓ Branch 2 taken 42254 times.
✓ Branch 3 taken 126 times.
✓ Branch 4 taken 34026 times.
✓ Branch 5 taken 8228 times.
172712 new_primary || add_fts_doc_id ||
3032 84508 innobase_need_rebuild(ha_alter_info, table, is_file_per_table);
3033
3034 /* Reserve one more space if new_primary is true, and we might
3035 need to add the FTS_DOC_ID_INDEX */
3036 176408 indexdef = index_defs = static_cast<ddl::Index_defn *>(mem_heap_alloc(
3037 heap, sizeof *indexdef *
3038
1/2
✓ Branch 0 taken 44102 times.
✗ Branch 1 not taken.
88204 (ha_alter_info->key_count + rebuild + got_default_clust)));
3039
3040
2/2
✓ Branch 0 taken 35874 times.
✓ Branch 1 taken 8228 times.
88204 if (rebuild) {
3041 ulint primary_key_number;
3042
3043
2/2
✓ Branch 0 taken 1722 times.
✓ Branch 1 taken 34152 times.
71748 if (new_primary) {
3044
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1722 times.
3444 if (n_add == 0) {
3045 assert(got_default_clust);
3046 assert(altered_table->s->primary_key == 0);
3047 primary_key_number = 0;
3048
2/2
✓ Branch 0 taken 97 times.
✓ Branch 1 taken 1625 times.
3444 } else if (ha_alter_info->handler_flags &
3049 Alter_inplace_info::ALTER_COLUMN_NOT_NULLABLE) {
3050 194 primary_key_number = altered_table->s->primary_key;
3051 } else {
3052 3250 primary_key_number = *add;
3053 }
3054
2/2
✓ Branch 0 taken 9798 times.
✓ Branch 1 taken 24354 times.
68304 } else if (got_default_clust) {
3055 /* Create the GEN_CLUST_INDEX */
3056 19596 ddl::Index_defn *index_def = indexdef++;
3057
3058 19596 index_def->m_fields = nullptr;
3059 19596 index_def->m_n_fields = 0;
3060 19596 index_def->m_ind_type = DICT_CLUSTERED;
3061 19596 index_def->m_name = innobase_index_reserve_name;
3062 19596 index_def->m_rebuild = true;
3063 19596 index_def->m_key_number = std::numeric_limits<size_t>::max();
3064 19596 index_def->m_is_ngram = false;
3065 19596 primary_key_number = ULINT_UNDEFINED;
3066 19596 goto created_clustered;
3067 } else {
3068 48708 primary_key_number = 0;
3069 }
3070
3071 /* Create the PRIMARY key index definition */
3072
1/2
✓ Branch 0 taken 26076 times.
✗ Branch 1 not taken.
52152 innobase_create_index_def(altered_table, new_dd_table, key_info,
3073 primary_key_number, true, true, indexdef++, heap);
3074
3075 71748 created_clustered:
3076 71748 n_add = 1;
3077
3078
2/2
✓ Branch 0 taken 37071 times.
✓ Branch 1 taken 35874 times.
145890 for (ulint i = 0; i < ha_alter_info->key_count; i++) {
3079
2/2
✓ Branch 0 taken 26076 times.
✓ Branch 1 taken 10995 times.
74142 if (i == primary_key_number) {
3080 52152 continue;
3081 }
3082 /* Copy the index definitions. */
3083
1/2
✓ Branch 0 taken 10995 times.
✗ Branch 1 not taken.
21990 innobase_create_index_def(altered_table, new_dd_table, key_info, i, true,
3084 false, indexdef, heap);
3085
3086
2/2
✓ Branch 0 taken 127 times.
✓ Branch 1 taken 10868 times.
21990 if (indexdef->m_ind_type & DICT_FTS) {
3087 254 n_fts_add++;
3088 }
3089
3090 21990 indexdef++;
3091 21990 n_add++;
3092 }
3093
3094
2/2
✓ Branch 0 taken 127 times.
✓ Branch 1 taken 35747 times.
71748 if (n_fts_add > 0) {
3095 254 ulint num_v = 0;
3096
3097
3/4
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 126 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 127 times.
256 if (!add_fts_doc_id &&
3098
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
2 !innobase_fts_check_doc_id_col(nullptr, altered_table,
3099 &fts_doc_id_col, &num_v)) {
3100 fts_doc_id_col = altered_table->s->fields - num_v;
3101 add_fts_doc_id = true;
3102 }
3103
3104
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 127 times.
254 if (!add_fts_doc_idx) {
3105 fts_doc_id_index_enum ret;
3106 ulint doc_col_no;
3107
3108 ret = innobase_fts_check_doc_id_index(nullptr, altered_table,
3109 &doc_col_no);
3110
3111 /* This should have been checked before */
3112 ut_ad(ret != FTS_INCORRECT_DOC_ID_INDEX);
3113
3114 if (ret == FTS_NOT_EXIST_DOC_ID_INDEX) {
3115 add_fts_doc_idx = true;
3116 } else {
3117 ut_ad(ret == FTS_EXIST_DOC_ID_INDEX);
3118 ut_ad(doc_col_no == ULINT_UNDEFINED || doc_col_no == fts_doc_id_col);
3119 }
3120 }
3121 }
3122 } else {
3123 /* Create definitions for added secondary indexes. */
3124
3125
2/2
✓ Branch 0 taken 8385 times.
✓ Branch 1 taken 8228 times.
33226 for (ulint i = 0; i < n_add; i++) {
3126
1/2
✓ Branch 0 taken 8385 times.
✗ Branch 1 not taken.
16770 innobase_create_index_def(altered_table, new_dd_table, key_info, add[i],
3127 false, false, indexdef, heap);
3128
3129
2/2
✓ Branch 0 taken 312 times.
✓ Branch 1 taken 8073 times.
16770 if (indexdef->m_ind_type & DICT_FTS) {
3130 624 n_fts_add++;
3131 }
3132
3133 16770 indexdef++;
3134 }
3135 }
3136
3137
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 44102 times.
88204 assert(index_defs + n_add == indexdef);
3138
3139
2/2
✓ Branch 0 taken 141 times.
✓ Branch 1 taken 43961 times.
88204 if (add_fts_doc_idx) {
3140 282 ddl::Index_defn *index_def = indexdef++;
3141
3142 282 index_def->m_fields = static_cast<ddl::Index_field *>(
3143
1/2
✓ Branch 0 taken 141 times.
✗ Branch 1 not taken.
282 mem_heap_alloc(heap, sizeof *index_def->m_fields));
3144
3145 282 index_def->m_n_fields = 1;
3146 282 index_def->m_fields->m_col_no = fts_doc_id_col;
3147 282 index_def->m_fields->m_prefix_len = 0;
3148 282 index_def->m_fields->m_is_ascending = true;
3149 282 index_def->m_fields->m_is_v_col = false;
3150 282 index_def->m_ind_type = DICT_UNIQUE;
3151
6/8
✓ Branch 0 taken 127 times.
✓ Branch 1 taken 14 times.
✓ Branch 2 taken 126 times.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 126 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 141 times.
282 ut_ad(!rebuild || !add_fts_doc_id ||
3152 fts_doc_id_col <= altered_table->s->fields);
3153
3154 282 index_def->m_name = FTS_DOC_ID_INDEX_NAME;
3155 282 index_def->m_is_ngram = false;
3156 282 index_def->m_rebuild = rebuild;
3157
3158 /* TODO: assign a real MySQL key number for this */
3159 282 index_def->m_key_number = ULINT_UNDEFINED;
3160 282 n_add++;
3161 }
3162
3163
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 44102 times.
88204 assert(indexdef > index_defs);
3164
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 44102 times.
88204 assert((ulint)(indexdef - index_defs) <=
3165 ha_alter_info->key_count + add_fts_doc_idx + got_default_clust);
3166
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 44102 times.
88204 assert(ha_alter_info->index_add_count <= n_add);
3167 88204 return index_defs;
3168 88204 }
3169
3170 /** Check each index column size, make sure they do not exceed the max limit
3171 @return true if index column size exceeds limit */
3172 10213 [[nodiscard]] static bool innobase_check_column_length(
3173 ulint max_col_len, /*!< in: maximum column length */
3174 const KEY *key_info) /*!< in: Indexes to be created */
3175 {
3176
2/2
✓ Branch 0 taken 14198 times.
✓ Branch 1 taken 10209 times.
24407 for (ulint key_part = 0; key_part < key_info->user_defined_key_parts;
3177 key_part++) {
3178
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 14194 times.
14198 if (key_info->key_part[key_part].length > max_col_len) {
3179 4 return (true);
3180 }
3181 }
3182 10209 return (false);
3183 }
3184
3185 /** Search for a given column in each index that is not being dropped. Return
3186 true if the column is part of any of the active indexes or it is a system
3187 column.
3188 @param[in] table table object
3189 @param[in] col_no column number of the column which is to be checked
3190 @param[in] is_v if this is a virtual column
3191 @retval true the column exists or it is a system column
3192 @retval false column does not exist */
3193 8084 static bool check_col_exists_in_indexes(const dict_table_t *table, ulint col_no,
3194 bool is_v) {
3195 /* This function does not check system columns */
3196
6/6
✓ Branch 0 taken 7918 times.
✓ Branch 1 taken 166 times.
✓ Branch 2 taken 2226 times.
✓ Branch 3 taken 5692 times.
✓ Branch 4 taken 2226 times.
✓ Branch 5 taken 5858 times.
8084 if (!is_v && table->get_col(col_no)->mtype == DATA_SYS) {
3197 2226 return (true);
3198 }
3199
3200
2/2
✓ Branch 0 taken 8825 times.
✓ Branch 1 taken 4887 times.
13712 for (const dict_index_t *index = table->first_index(); index;
3201 7854 index = index->next()) {
3202
2/2
✓ Branch 0 taken 5790 times.
✓ Branch 1 taken 7854 times.
13644 for (ulint i = 0; i < index->n_user_defined_cols; i++) {
3203 5790 const dict_col_t *idx_col = index->get_col(i);
3204
3205
6/6
✓ Branch 0 taken 244 times.
✓ Branch 1 taken 5546 times.
✓ Branch 2 taken 142 times.
✓ Branch 3 taken 102 times.
✓ Branch 4 taken 142 times.
✓ Branch 5 taken 5648 times.
5790 if (is_v && idx_col->is_virtual()) {
3206 142 const dict_v_col_t *v_col =
3207 reinterpret_cast<const dict_v_col_t *>(idx_col);
3208
2/2
✓ Branch 0 taken 27 times.
✓ Branch 1 taken 115 times.
142 if (v_col->v_pos == col_no) {
3209 27 return (true);
3210 }
3211 }
3212
3213
6/6
✓ Branch 0 taken 5546 times.
✓ Branch 1 taken 217 times.
✓ Branch 2 taken 5493 times.
✓ Branch 3 taken 53 times.
✓ Branch 4 taken 944 times.
✓ Branch 5 taken 4819 times.
11256 if (!is_v && !idx_col->is_virtual() &&
3214
2/2
✓ Branch 0 taken 944 times.
✓ Branch 1 taken 4549 times.
5493 dict_col_get_no(idx_col) == col_no) {
3215 944 return (true);
3216 }
3217 }
3218 }
3219
3220 4887 return (false);
3221 }
3222
3223 /** Reset dict_col_t::ord_part for those columns that fail to be indexed,
3224 Check every existing column to see if any current index references them.
3225 This should be checked after an index is dropped during ALTER TABLE.
3226 @param[in,out] table InnoDB table to check */
3227 742 static inline void reset_column_ord_part(dict_table_t *table) {
3228
2/2
✓ Branch 0 taken 4115 times.
✓ Branch 1 taken 742 times.
4857 for (ulint i = 0; i < table->get_n_cols(); i++) {
3229
2/2
✓ Branch 0 taken 1209 times.
✓ Branch 1 taken 2906 times.
4115 if (!check_col_exists_in_indexes(table, i, false)) {
3230 1209 table->cols[i].ord_part = 0;
3231 }
3232 }
3233
3234
2/2
✓ Branch 0 taken 45 times.
✓ Branch 1 taken 742 times.
787 for (ulint i = 0; i < dict_table_get_n_v_cols(table); i++) {
3235
2/2
✓ Branch 0 taken 29 times.
✓ Branch 1 taken 16 times.
45 if (!check_col_exists_in_indexes(table, i, true)) {
3236 29 table->v_cols[i].m_col.ord_part = 0;
3237 }
3238 }
3239 742 }
3240
3241 /** Drop in-memory metadata for index (dict_index_t) left from previous
3242 online ALTER operation.
3243 @param[in] table table to check
3244 @param[in] locked if it is dict_sys mutex locked */
3245 60100 static void online_retry_drop_dict_indexes(dict_table_t *table, bool locked) {
3246
2/2
✓ Branch 0 taken 16000 times.
✓ Branch 1 taken 44100 times.
60100 if (!locked) {
3247 16000 dict_sys_mutex_enter();
3248 }
3249
3250 60100 bool modify = false;
3251 60100 dict_index_t *index = table->first_index();
3252
3253
2/2
✓ Branch 0 taken 28930 times.
✓ Branch 1 taken 60100 times.
89030 for (index = index->next(); index != nullptr; index = index->next()) {
3254
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 28925 times.
28930 if (dict_index_get_online_status(index) == ONLINE_INDEX_ABORTED_DROPPED) {
3255 5 dict_index_t *prev = UT_LIST_GET_PREV(indexes, index);
3256
3257 5 dict_index_remove_from_cache(table, index);
3258
3259 5 index = prev;
3260
3261 5 modify = true;
3262 }
3263 }
3264
3265
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 60095 times.
60100 if (modify) {
3266 /* Since the table has been modified, table->def_trx_id should be
3267 adjusted like ddl::drop_indexes(). However, this function may
3268 be called before the DDL transaction starts, so it is impossible to
3269 get current DDL transaction ID. Thus advancing def_trx_id by 1 to
3270 simply inform other threads about this change. */
3271 5 ++table->def_trx_id;
3272
3273 5 reset_column_ord_part(table);
3274 }
3275
3276
2/2
✓ Branch 0 taken 16000 times.
✓ Branch 1 taken 44100 times.
60100 if (!locked) {
3277 16000 dict_sys_mutex_exit();
3278 }
3279 60100 }
3280
3281 /** Determines if InnoDB is dropping a foreign key constraint.
3282 @param foreign the constraint
3283 @param drop_fk constraints being dropped
3284 @param n_drop_fk number of constraints that are being dropped
3285 @return whether the constraint is being dropped */
3286 22 [[nodiscard]] inline bool innobase_dropping_foreign(
3287 const dict_foreign_t *foreign, dict_foreign_t **drop_fk, ulint n_drop_fk) {
3288
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 20 times.
22 while (n_drop_fk--) {
3289
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (*drop_fk++ == foreign) {
3290 2 return (true);
3291 }
3292 }
3293
3294 20 return (false);
3295 }
3296
3297 /** Convert a default value for ADD COLUMN.
3298
3299 @param heap Memory heap where allocated
3300 @param dfield InnoDB data field to copy to
3301 @param field MySQL value for the column
3302 @param comp nonzero if in compact format */
3303 17593 static void innobase_build_col_map_add(mem_heap_t *heap, dfield_t *dfield,
3304 const Field *field, ulint comp,
3305 row_prebuilt_t *prebuilt) {
3306
2/2
✓ Branch 0 taken 13787 times.
✓ Branch 1 taken 3806 times.
17593 if (field->is_real_null()) {
3307 13787 dfield_set_null(dfield);
3308 13787 return;
3309 }
3310
3311 3806 ulint size = field->pack_length();
3312
3313 3806 byte *buf = static_cast<byte *>(mem_heap_alloc(heap, size));
3314
3315 3806 const byte *mysql_data = field->field_ptr();
3316
3317 3806 row_mysql_store_col_in_innobase_format(
3318 dfield, buf, true, mysql_data, size, comp,
3319 3806 field->column_format() == COLUMN_FORMAT_TYPE_COMPRESSED,
3320 3806 reinterpret_cast<const byte *>(field->zip_dict_data.str),
3321 3806 field->zip_dict_data.length, &prebuilt->compress_heap);
3322 }
3323
3324 /** Construct the translation table for reordering, dropping or
3325 adding columns.
3326
3327 @param ha_alter_info Data used during in-place alter
3328 @param altered_table MySQL table that is being altered
3329 @param table MySQL table as it is before the ALTER operation
3330 @param new_table InnoDB table corresponding to MySQL altered_table
3331 @param old_table InnoDB table corresponding to MYSQL table
3332 @param add_cols Default values for ADD COLUMN, or NULL if no ADD COLUMN
3333 @param heap Memory heap where allocated
3334 @return array of integers, mapping column numbers in the table
3335 to column numbers in altered_table */
3336 35854 [[nodiscard]] static const ulint *innobase_build_col_map(
3337 Alter_inplace_info *ha_alter_info, const TABLE *altered_table,
3338 const TABLE *table, const dict_table_t *new_table,
3339 const dict_table_t *old_table, dtuple_t *add_cols, mem_heap_t *heap,
3340 row_prebuilt_t *prebuilt) {
3341
1/2
✓ Branch 0 taken 35854 times.
✗ Branch 1 not taken.
35854 DBUG_TRACE;
3342
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 35854 times.
35854 assert(altered_table != table);
3343
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 35854 times.
35854 assert(new_table != old_table);
3344
3/6
✓ Branch 0 taken 35854 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 35854 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 35854 times.
35854 assert(new_table->get_n_cols() + dict_table_get_n_v_cols(new_table) >=
3345 altered_table->s->fields + DATA_N_SYS_COLS);
3346
3/6
✓ Branch 0 taken 35854 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 35854 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 35854 times.
35854 assert(old_table->get_n_cols() + dict_table_get_n_v_cols(old_table) >=
3347 table->s->fields + DATA_N_SYS_COLS);
3348
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 35854 times.
35854 assert(!!add_cols ==
3349 !!(ha_alter_info->handler_flags & Alter_inplace_info::ADD_COLUMN));
3350
5/8
✓ Branch 0 taken 9204 times.
✓ Branch 1 taken 26650 times.
✓ Branch 2 taken 9204 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 9204 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 9204 times.
35854 assert(!add_cols || dtuple_get_n_fields(add_cols) == new_table->get_n_cols());
3351
3352 71708 ulint *col_map = static_cast<ulint *>(mem_heap_alloc(
3353
1/2
✓ Branch 0 taken 35854 times.
✗ Branch 1 not taken.
35854 heap, (old_table->n_cols + old_table->n_v_cols) * sizeof *col_map));
3354
3355 List_iterator_fast<Create_field> cf_it(
3356
1/2
✓ Branch 0 taken 35854 times.
✗ Branch 1 not taken.
35854 ha_alter_info->alter_info->create_list);
3357 35854 uint i = 0;
3358 35854 uint num_v = 0;
3359
3360 /* Any dropped columns will map to ULINT_UNDEFINED. */
3361
2/2
✓ Branch 0 taken 235159 times.
✓ Branch 1 taken 35854 times.
271013 for (uint old_i = 0; old_i + DATA_N_SYS_COLS < old_table->n_cols; old_i++) {
3362 235159 col_map[old_i] = ULINT_UNDEFINED;
3363 }
3364
3365
2/2
✓ Branch 0 taken 914 times.
✓ Branch 1 taken 35854 times.
36768 for (uint old_i = 0; old_i < old_table->n_v_cols; old_i++) {
3366 914 col_map[old_i + old_table->n_cols] = ULINT_UNDEFINED;
3367 }
3368
3369
2/2
✓ Branch 0 taken 252113 times.
✓ Branch 1 taken 35854 times.
287967 while (const Create_field *new_field = cf_it++) {
3370 252113 bool is_v = false;
3371
3372
4/4
✓ Branch 0 taken 936 times.
✓ Branch 1 taken 251177 times.
✓ Branch 2 taken 914 times.
✓ Branch 3 taken 22 times.
252113 if (innobase_is_v_fld(new_field)) {
3373 914 is_v = true;
3374 }
3375
3376 252113 ulint num_old_v = 0;
3377
3378
2/2
✓ Branch 0 taken 3138115 times.
✓ Branch 1 taken 17593 times.
3155708 for (uint old_i = 0; table->field[old_i]; old_i++) {
3379 3138115 const Field *field = table->field[old_i];
3380
4/4
✓ Branch 0 taken 1030 times.
✓ Branch 1 taken 3137085 times.
✓ Branch 2 taken 970 times.
✓ Branch 3 taken 60 times.
3138115 if (innobase_is_v_fld(field)) {
3381
4/4
✓ Branch 0 taken 933 times.
✓ Branch 1 taken 37 times.
✓ Branch 2 taken 914 times.
✓ Branch 3 taken 19 times.
970 if (is_v && new_field->field == field) {
3382 914 col_map[old_table->n_cols + num_v] = num_old_v;
3383 914 num_old_v++;
3384 914 goto found_col;
3385 }
3386 56 num_old_v++;
3387 56 continue;
3388 }
3389
3390
2/2
✓ Branch 0 taken 233606 times.
✓ Branch 1 taken 2903539 times.
3137145 if (new_field->field == field) {
3391 233606 col_map[old_i - num_old_v] = i;
3392 233606 goto found_col;
3393 }
3394 }
3395
3396
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 17593 times.
17593 ut_ad(!is_v);
3397
1/2
✓ Branch 0 taken 17593 times.
✗ Branch 1 not taken.
17593 innobase_build_col_map_add(heap, dtuple_get_nth_field(add_cols, i),
3398 17593 altered_table->field[i + num_v],
3399
2/4
✓ Branch 0 taken 17593 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 17593 times.
✗ Branch 3 not taken.
17593 dict_table_is_comp(new_table), prebuilt);
3400 252113 found_col:
3401
2/2
✓ Branch 0 taken 914 times.
✓ Branch 1 taken 251199 times.
252113 if (is_v) {
3402 914 num_v++;
3403 } else {
3404 251199 i++;
3405 }
3406 252113 }
3407
3408
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 35854 times.
35854 assert(i == altered_table->s->fields - num_v);
3409
3410 35854 i = table->s->fields - old_table->n_v_cols;
3411
3412 /* Add the InnoDB hidden FTS_DOC_ID column, if any. */
3413
2/2
✓ Branch 0 taken 19 times.
✓ Branch 1 taken 35835 times.
35854 if (i + DATA_N_SYS_COLS < old_table->n_cols) {
3414 /* There should be exactly one extra field,
3415 the FTS_DOC_ID. */
3416
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 19 times.
19 assert(DICT_TF2_FLAG_IS_SET(old_table, DICT_TF2_FTS_HAS_DOC_ID));
3417
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 19 times.
19 assert(i + DATA_N_SYS_COLS + 1 == old_table->n_cols);
3418
2/4
✓ Branch 0 taken 19 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 19 times.
19 assert(!strcmp(old_table->get_col_name(i), FTS_DOC_ID_COL_NAME));
3419 19 if (altered_table->s->fields + DATA_N_SYS_COLS - new_table->n_v_cols <
3420
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 19 times.
19 new_table->n_cols) {
3421 assert(DICT_TF2_FLAG_IS_SET(new_table, DICT_TF2_FTS_HAS_DOC_ID));
3422 assert(altered_table->s->fields + DATA_N_SYS_COLS + 1 ==
3423 static_cast<ulint>(new_table->n_cols + new_table->n_v_cols));
3424 col_map[i] = altered_table->s->fields - new_table->n_v_cols;
3425 } else {
3426
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 19 times.
19 assert(!DICT_TF2_FLAG_IS_SET(new_table, DICT_TF2_FTS_HAS_DOC_ID));
3427 19 col_map[i] = ULINT_UNDEFINED;
3428 }
3429
3430 19 i++;
3431 } else {
3432
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 35835 times.
35835 assert(!DICT_TF2_FLAG_IS_SET(old_table, DICT_TF2_FTS_HAS_DOC_ID));
3433 }
3434
3435
2/2
✓ Branch 0 taken 107562 times.
✓ Branch 1 taken 35854 times.
143416 for (; i < old_table->n_cols; i++) {
3436 107562 col_map[i] = i + new_table->n_cols - old_table->n_cols;
3437 }
3438
3439 35854 return col_map;
3440 35854 }
3441
3442 /** Drop newly create FTS index related auxiliary table during
3443 FIC create index process, before fts_add_index is called
3444 @param table table that was being rebuilt online
3445 @param trx transaction
3446 @return DB_SUCCESS if successful, otherwise last error code
3447 */
3448 4 static dberr_t innobase_drop_fts_index_table(dict_table_t *table, trx_t *trx) {
3449 4 dberr_t ret_err = DB_SUCCESS;
3450
3451
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 4 times.
16 for (dict_index_t *index = table->first_index(); index != nullptr;
3452 12 index = index->next()) {
3453
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 8 times.
12 if (index->type & DICT_FTS) {
3454 dberr_t err;
3455
3456 4 err = fts_drop_index_tables(trx, index, nullptr);
3457
3458
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 if (err != DB_SUCCESS) {
3459 ret_err = err;
3460 }
3461 }
3462 }
3463
3464 4 return (ret_err);
3465 }
3466
3467 /** Get the new non-virtual column names if any columns were renamed
3468 @param ha_alter_info Data used during in-place alter
3469 @param altered_table MySQL table that is being altered
3470 @param table MySQL table as it is before the ALTER operation
3471 @param user_table InnoDB table as it is before the ALTER operation
3472 @param heap Memory heap for the allocation
3473 @return array of new column names in rebuilt_table, or NULL if not renamed */
3474 101 [[nodiscard]] static const char **innobase_get_col_names(
3475 Alter_inplace_info *ha_alter_info, const TABLE *altered_table,
3476 const TABLE *table, const dict_table_t *user_table, mem_heap_t *heap) {
3477 const char **cols;
3478 uint i;
3479
3480
1/2
✓ Branch 0 taken 101 times.
✗ Branch 1 not taken.
101 DBUG_TRACE;
3481
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 101 times.
101 assert(user_table->n_t_def > table->s->fields);
3482
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 101 times.
101 assert(ha_alter_info->handler_flags & Alter_inplace_info::ALTER_COLUMN_NAME);
3483
3484 cols = static_cast<const char **>(
3485
1/2
✓ Branch 0 taken 101 times.
✗ Branch 1 not taken.
101 mem_heap_zalloc(heap, user_table->n_def * sizeof *cols));
3486
3487 101 i = 0;
3488 List_iterator_fast<Create_field> cf_it(
3489
1/2
✓ Branch 0 taken 101 times.
✗ Branch 1 not taken.
101 ha_alter_info->alter_info->create_list);
3490
2/2
✓ Branch 0 taken 375 times.
✓ Branch 1 taken 101 times.
476 while (const Create_field *new_field = cf_it++) {
3491 375 ulint num_v = 0;
3492
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 375 times.
375 assert(i < altered_table->s->fields);
3493
3494
3/4
✓ Branch 0 taken 44 times.
✓ Branch 1 taken 331 times.
✓ Branch 2 taken 44 times.
✗ Branch 3 not taken.
375 if (innobase_is_v_fld(new_field)) {
3495 44 continue;
3496 }
3497
3498
2/2
✓ Branch 0 taken 954 times.
✓ Branch 1 taken 18 times.
972 for (uint old_i = 0; table->field[old_i]; old_i++) {
3499
3/4
✓ Branch 0 taken 34 times.
✓ Branch 1 taken 920 times.
✓ Branch 2 taken 34 times.
✗ Branch 3 not taken.
954 if (innobase_is_v_fld(table->field[old_i])) {
3500 34 num_v++;
3501 }
3502
3503
2/2
✓ Branch 0 taken 313 times.
✓ Branch 1 taken 641 times.
954 if (new_field->field == table->field[old_i]) {
3504 313 cols[old_i - num_v] = new_field->field_name;
3505 313 break;
3506 }
3507 }
3508
3509 331 i++;
3510 375 }
3511
3512 /* Copy the internal column names. */
3513 101 i = table->s->fields - user_table->n_v_def;
3514
1/2
✓ Branch 0 taken 101 times.
✗ Branch 1 not taken.
101 cols[i] = user_table->get_col_name(i);
3515
3516
2/2
✓ Branch 0 taken 206 times.
✓ Branch 1 taken 101 times.
307 while (++i < user_table->n_def) {
3517 206 cols[i] = cols[i - 1] + strlen(cols[i - 1]) + 1;
3518 }
3519
3520 101 return cols;
3521 101 }
3522
3523 /** Check whether the column prefix is increased, decreased, or unchanged.
3524 @param[in] new_prefix_len new prefix length
3525 @param[in] old_prefix_len new prefix length
3526 @retval 1 prefix is increased
3527 @retval 0 prefix is unchanged
3528 @retval -1 prefix is decreased */
3529 45108 static inline lint innobase_pk_col_prefix_compare(ulint new_prefix_len,
3530 ulint old_prefix_len) {
3531
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 45108 times.
45108 ut_ad(new_prefix_len < REC_MAX_DATA_SIZE);
3532
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 45108 times.
45108 ut_ad(old_prefix_len < REC_MAX_DATA_SIZE);
3533
3534
2/2
✓ Branch 0 taken 45041 times.
✓ Branch 1 taken 67 times.
45108 if (new_prefix_len == old_prefix_len) {
3535 45041 return (0);
3536 }
3537
3538
2/2
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 52 times.
67 if (new_prefix_len == 0) {
3539 15 new_prefix_len = ULINT_MAX;
3540 }
3541
3542
2/2
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 51 times.
67 if (old_prefix_len == 0) {
3543 16 old_prefix_len = ULINT_MAX;
3544 }
3545
3546
2/2
✓ Branch 0 taken 31 times.
✓ Branch 1 taken 36 times.
67 if (new_prefix_len > old_prefix_len) {
3547 31 return (1);
3548 } else {
3549 36 return (-1);
3550 }
3551 }
3552
3553 /** Check whether the column is existing in old table.
3554 @param[in] new_col_no new column no
3555 @param[in] col_map mapping of old column numbers to new ones
3556 @param[in] col_map_size the column map size
3557 @return true if the column is existing, otherwise false. */
3558 132 static inline bool innobase_pk_col_is_existing(const ulint new_col_no,
3559 const ulint *col_map,
3560 const ulint col_map_size) {
3561
2/2
✓ Branch 0 taken 600 times.
✓ Branch 1 taken 73 times.
673 for (ulint i = 0; i < col_map_size; i++) {
3562
2/2
✓ Branch 0 taken 59 times.
✓ Branch 1 taken 541 times.
600 if (col_map[i] == new_col_no) {
3563 59 return (true);
3564 }
3565 }
3566
3567 73 return (false);
3568 }
3569
3570 /** Determine whether both the indexes have same set of primary key
3571 fields arranged in the same order.
3572
3573 Rules when we cannot skip sorting:
3574 (1) Removing existing PK columns somewhere else than at the end of the PK;
3575 (2) Adding existing columns to the PK, except at the end of the PK when no
3576 columns are removed from the PK;
3577 (3) Changing the order of existing PK columns;
3578 (4) Decreasing the prefix length just like removing existing PK columns
3579 follows rule(1), Increasing the prefix length just like adding existing
3580 PK columns follows rule(2);
3581 (5) Changing the ascending order of the existing PK columns.
3582 @param[in] col_map mapping of old column numbers to new ones
3583 @param[in] old_clust_index index to be compared
3584 @param[in] new_clust_index index to be compared
3585 @retval true if both indexes have same order.
3586 @retval false. */
3587 35798 [[nodiscard]] static bool innobase_pk_order_preserved(
3588 const ulint *col_map, const dict_index_t *old_clust_index,
3589 const dict_index_t *new_clust_index) {
3590 35798 ulint old_n_uniq = dict_index_get_n_ordering_defined_by_user(old_clust_index);
3591 35798 ulint new_n_uniq = dict_index_get_n_ordering_defined_by_user(new_clust_index);
3592
3593
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 35798 times.
35798 ut_ad(old_clust_index->is_clustered());
3594
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 35798 times.
35798 ut_ad(new_clust_index->is_clustered());
3595
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 35798 times.
35798 ut_ad(old_clust_index->table != new_clust_index->table);
3596
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 35798 times.
35798 ut_ad(col_map != nullptr);
3597
3598
2/2
✓ Branch 0 taken 10851 times.
✓ Branch 1 taken 24947 times.
35798 if (old_n_uniq == 0) {
3599 /* There was no PRIMARY KEY in the table.
3600 If there is no PRIMARY KEY after the ALTER either,
3601 no sorting is needed. */
3602 10851 return (new_n_uniq == old_n_uniq);
3603 }
3604
3605 /* DROP PRIMARY KEY is only allowed in combination with
3606 ADD PRIMARY KEY. */
3607
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 24947 times.
24947 ut_ad(new_n_uniq > 0);
3608
3609 /* The order of the last processed new_clust_index key field,
3610 not counting ADD COLUMN, which are constant. */
3611 24947 lint last_field_order = -1;
3612 24947 ulint existing_field_count = 0;
3613 24947 ulint old_n_cols = old_clust_index->table->get_n_cols();
3614
2/2
✓ Branch 0 taken 45738 times.
✓ Branch 1 taken 24410 times.
70148 for (ulint new_field = 0; new_field < new_n_uniq; new_field++) {
3615 45738 ulint new_col_no = new_clust_index->fields[new_field].col->ind;
3616
3617 /* Check if there is a match in old primary key. */
3618 45738 ulint old_field = 0;
3619
2/2
✓ Branch 0 taken 82368 times.
✓ Branch 1 taken 132 times.
82500 while (old_field < old_n_uniq) {
3620 82368 ulint old_col_no = old_clust_index->fields[old_field].col->ind;
3621
3622
2/2
✓ Branch 0 taken 45606 times.
✓ Branch 1 taken 36762 times.
82368 if (col_map[old_col_no] == new_col_no) {
3623 45606 break;
3624 }
3625
3626 36762 old_field++;
3627 }
3628
3629 /* The order of key field in the new primary key.
3630 1. old PK column: idx in old primary key
3631 2. existing column: old_n_uniq + sequence no
3632 3. newly added column: no order */
3633 lint new_field_order;
3634 45738 const bool old_pk_column = old_field < old_n_uniq;
3635
3636
2/2
✓ Branch 0 taken 45606 times.
✓ Branch 1 taken 132 times.
45738 if (old_pk_column) {
3637 45606 new_field_order = old_field;
3638
2/2
✓ Branch 0 taken 59 times.
✓ Branch 1 taken 73 times.
132 } else if (innobase_pk_col_is_existing(new_col_no, col_map, old_n_cols)) {
3639 59 new_field_order = old_n_uniq + existing_field_count++;
3640 } else {
3641 /* Skip newly added column. */
3642 73 continue;
3643 }
3644
3645
2/2
✓ Branch 0 taken 528 times.
✓ Branch 1 taken 45137 times.
45665 if (last_field_order + 1 != new_field_order) {
3646 /* Old PK order is not kept, or existing column
3647 is not added at the end of old PK. */
3648 528 return (false);
3649 }
3650
3651 45137 last_field_order = new_field_order;
3652
3653
2/2
✓ Branch 0 taken 29 times.
✓ Branch 1 taken 45108 times.
45137 if (!old_pk_column) {
3654 29 continue;
3655 }
3656
3657 /* Check prefix length change. */
3658 90216 const lint prefix_change = innobase_pk_col_prefix_compare(
3659 45108 new_clust_index->fields[new_field].prefix_len,
3660 45108 old_clust_index->fields[old_field].prefix_len);
3661
3662
2/2
✓ Branch 0 taken 36 times.
✓ Branch 1 taken 45072 times.
45108 if (prefix_change < 0) {
3663 /* If a column's prefix length is decreased, it should
3664 be the last old PK column in new PK.
3665 Note: we set last_field_order to -2, so that if there
3666 are any old PK colmns or existing columns after it in
3667 new PK, the comparison to new_field_order will fail in
3668 the next round.*/
3669 36 last_field_order = -2;
3670
2/2
✓ Branch 0 taken 31 times.
✓ Branch 1 taken 45041 times.
45072 } else if (prefix_change > 0) {
3671 /* If a column's prefix length is increased, it should
3672 be the last PK column in old PK. */
3673
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 24 times.
31 if (old_field != old_n_uniq - 1) {
3674 7 return (false);
3675 }
3676 }
3677
3678 /* Check new primary key field ascending or descending changes
3679 compared to old primary key field. */
3680 45101 bool change_asc = (new_clust_index->fields[new_field].is_ascending ==
3681 45101 old_clust_index->fields[old_field].is_ascending);
3682
3683
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 45099 times.
45101 if (!change_asc) {
3684 2 return (false);
3685 }
3686 }
3687
3688 24410 return (true);
3689 }
3690
3691 /** Check if we are creating spatial indexes on GIS columns, which are
3692 legacy columns from earlier MySQL, such as 5.6. If so, we have to update
3693 the mtypes of the old GIS columns to DATA_GEOMETRY.
3694 In 5.6, we store GIS columns as DATA_BLOB in InnoDB layer, it will introduce
3695 confusion when we run latest server on older data. That's why we need to
3696 do the upgrade.
3697 @param[in] ha_alter_info Data used during in-place alter
3698 @param[in] table Table on which we want to add indexes
3699 @return DB_SUCCESS if update successfully or no columns need to be updated,
3700 otherwise DB_ERROR, which means we can't update the mtype for some
3701 column, and creating spatial index on it should be dangerous */
3702 8221 static dberr_t innobase_check_gis_columns(Alter_inplace_info *ha_alter_info,
3703 dict_table_t *table) {
3704
1/2
✓ Branch 0 taken 8221 times.
✗ Branch 1 not taken.
8221 DBUG_TRACE;
3705
3706
2/2
✓ Branch 0 taken 8376 times.
✓ Branch 1 taken 8221 times.
16597 for (uint key_num = 0; key_num < ha_alter_info->index_add_count; key_num++) {
3707 8376 const KEY &key =
3708 ha_alter_info
3709 8376 ->key_info_buffer[ha_alter_info->index_add_buffer[key_num]];
3710
3711
2/2
✓ Branch 0 taken 8286 times.
✓ Branch 1 taken 90 times.
8376 if (!(key.flags & HA_SPATIAL)) {
3712 8376 continue;
3713 }
3714
3715
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 90 times.
90 ut_ad(key.user_defined_key_parts == 1);
3716 90 const KEY_PART_INFO &key_part = key.key_part[0];
3717
3718 /* Does not support spatial index on virtual columns */
3719
3/4
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 85 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 5 times.
90 if (innobase_is_v_fld(key_part.field)) {
3720 return DB_UNSUPPORTED;
3721 }
3722
3723 180 ulint col_nr = dict_table_has_column(table, key_part.field->field_name,
3724
1/2
✓ Branch 0 taken 90 times.
✗ Branch 1 not taken.
90 key_part.fieldnr);
3725
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 90 times.
90 ut_ad(col_nr != table->n_def);
3726 90 dict_col_t *col = &table->cols[col_nr];
3727
3728
1/2
✓ Branch 0 taken 90 times.
✗ Branch 1 not taken.
90 if (col->mtype != DATA_BLOB) {
3729
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 90 times.
90 ut_ad(DATA_GEOMETRY_MTYPE(col->mtype));
3730 90 continue;
3731 }
3732
3733 const char *col_name = table->get_col_name(col_nr);
3734 col->mtype = DATA_GEOMETRY;
3735
3736 ib::info(ER_IB_MSG_598)
3737 << "Updated mtype of column" << col_name << " in table " << table->name
3738 << ", whose id is " << table->id << " to DATA_GEOMETRY";
3739 }
3740
3741 8221 return DB_SUCCESS;
3742 8221 }
3743
3744 /** Update the attributes for the implicit tablespaces
3745 @param[in] thd THD object
3746 @param[in] ha_alter_info Data used during in-place alter
3747 @param[in] table MySQL table that is being modified
3748 @return true Failure
3749 @return false Success */
3750 5035 static bool prepare_inplace_change_implicit_tablespace_option(
3751 THD *thd, Alter_inplace_info *ha_alter_info, const dict_table_t *table) {
3752
1/2
✓ Branch 0 taken 5035 times.
✗ Branch 1 not taken.
5035 dd::cache::Dictionary_client *client = dd::get_dd_client(thd);
3753
1/2
✓ Branch 0 taken 5035 times.
✗ Branch 1 not taken.
5035 dd::cache::Dictionary_client::Auto_releaser releaser(client);
3754
3755 5035 dd::Object_id space_id = table->dd_space_id;
3756
3757
1/2
✓ Branch 0 taken 5035 times.
✗ Branch 1 not taken.
5035 return dd_implicit_alter_tablespace(client, space_id,
3758 10070 ha_alter_info->create_info);
3759 5035 }
3760
3761 /** Collect virtual column info for its addition
3762 @param[in] ha_alter_info Data used during in-place alter
3763 @param[in] altered_table MySQL table that is being altered to
3764 @param[in] table MySQL table as it is before the ALTER operation
3765 @retval true Failure
3766 @retval false Success */
3767 289 static bool prepare_inplace_add_virtual(Alter_inplace_info *ha_alter_info,
3768 const TABLE *altered_table,
3769 const TABLE *table) {
3770 ha_innobase_inplace_ctx *ctx;
3771 289 ulint i = 0;
3772 289 ulint j = 0;
3773 const Create_field *new_field;
3774
3775 289 ctx = static_cast<ha_innobase_inplace_ctx *>(ha_alter_info->handler_ctx);
3776
3777 578 ctx->add_vcol = static_cast<dict_v_col_t *>(mem_heap_zalloc(
3778 ctx->heap,
3779
1/2
✓ Branch 0 taken 289 times.
✗ Branch 1 not taken.
289 ha_alter_info->virtual_column_add_count * sizeof *ctx->add_vcol));
3780 578 ctx->add_vcol_name = static_cast<const char **>(mem_heap_alloc(
3781 ctx->heap,
3782
1/2
✓ Branch 0 taken 289 times.
✗ Branch 1 not taken.
289 ha_alter_info->virtual_column_add_count * sizeof *ctx->add_vcol_name));
3783
3784 List_iterator_fast<Create_field> cf_it(
3785
1/2
✓ Branch 0 taken 289 times.
✗ Branch 1 not taken.
289 ha_alter_info->alter_info->create_list);
3786
3787
2/2
✓ Branch 0 taken 1415 times.
✓ Branch 1 taken 288 times.
1703 while ((new_field = (cf_it++)) != nullptr) {
3788 1415 const Field *field = new_field->field;
3789 ulint old_i;
3790
3791
2/2
✓ Branch 0 taken 4281 times.
✓ Branch 1 taken 302 times.
4583 for (old_i = 0; table->field[old_i]; old_i++) {
3792 4281 const Field *n_field = table->field[old_i];
3793
2/2
✓ Branch 0 taken 1113 times.
✓ Branch 1 taken 3168 times.
4281 if (field == n_field) {
3794 1113 break;
3795 }
3796 }
3797
3798 1415 i++;
3799
3800
2/2
✓ Branch 0 taken 1113 times.
✓ Branch 1 taken 302 times.
1415 if (table->field[old_i]) {
3801 1113 continue;
3802 }
3803
3804
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 302 times.
302 ut_ad(!field);
3805
3806 ulint col_len;
3807 ulint is_unsigned;
3808 ulint field_type;
3809 ulint charset_no;
3810
3811 302 field = altered_table->field[i - 1];
3812
3813
1/2
✓ Branch 0 taken 302 times.
✗ Branch 1 not taken.
302 ulint col_type = get_innobase_type_from_mysql_type(&is_unsigned, field);
3814
1/2
✓ Branch 0 taken 302 times.
✗ Branch 1 not taken.
302 bool is_multi_value = innobase_is_multi_value_fld(field);
3815
3816
2/4
✓ Branch 0 taken 302 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 302 times.
302 if (!field->gcol_info || field->stored_in_db) {
3817 my_error(ER_WRONG_KEY_COLUMN, MYF(0), field->field_name);
3818 1 return (true);
3819 }
3820
3821
2/2
✓ Branch 0 taken 96 times.
✓ Branch 1 taken 206 times.
302 if (is_multi_value) {
3822
1/2
✓ Branch 0 taken 96 times.
✗ Branch 1 not taken.
96 col_len = field->key_length();
3823 } else {
3824
1/2
✓ Branch 0 taken 206 times.
✗ Branch 1 not taken.
206 col_len = field->pack_length();
3825 }
3826
1/2
✓ Branch 0 taken 302 times.
✗ Branch 1 not taken.
302 field_type = (ulint)field->type();
3827
3828
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 301 times.
302 if (!field->is_nullable()) {
3829 1 field_type |= DATA_NOT_NULL;
3830 }
3831
3832
3/4
✓ Branch 0 taken 302 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 277 times.
✓ Branch 3 taken 25 times.
302 if (field->binary()) {
3833 277 field_type |= DATA_BINARY_TYPE;
3834 }
3835
3836
2/2
✓ Branch 0 taken 26 times.
✓ Branch 1 taken 276 times.
302 if (is_unsigned) {
3837 26 field_type |= DATA_UNSIGNED;
3838 }
3839
3840
3/4
✓ Branch 0 taken 302 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 69 times.
✓ Branch 3 taken 233 times.
302 if (dtype_is_string_type(col_type)) {
3841
1/2
✓ Branch 0 taken 69 times.
✗ Branch 1 not taken.
69 charset_no = (ulint)field->charset()->number;
3842
3843
3/4
✓ Branch 0 taken 69 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 68 times.
69 DBUG_EXECUTE_IF("ib_alter_add_virtual_fail",
3844 charset_no += MAX_CHAR_COLL_NUM;);
3845
3846
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 68 times.
69 if (charset_no > MAX_CHAR_COLL_NUM) {
3847
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 my_error(ER_WRONG_KEY_COLUMN, MYF(0), field->field_name);
3848 1 return (true);
3849 }
3850 } else {
3851 233 charset_no = 0;
3852 }
3853
3854
7/8
✓ Branch 0 taken 301 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 28 times.
✓ Branch 3 taken 273 times.
✓ Branch 4 taken 7 times.
✓ Branch 5 taken 21 times.
✓ Branch 6 taken 7 times.
✓ Branch 7 taken 294 times.
301 if (field->type() == MYSQL_TYPE_VARCHAR && !is_multi_value) {
3855
1/2
✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
7 uint32_t length_bytes = field->get_length_bytes();
3856
3857 7 col_len -= length_bytes;
3858
3859
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 5 times.
7 if (length_bytes == 2) {
3860 2 field_type |= DATA_LONG_TRUE_VARCHAR;
3861 }
3862 }
3863
3864
1/2
✓ Branch 0 taken 301 times.
✗ Branch 1 not taken.
301 ctx->add_vcol[j].m_col.prtype = dtype_form_prtype(field_type, charset_no);
3865
3866 301 ctx->add_vcol[j].m_col.prtype |= DATA_VIRTUAL;
3867
3868
2/2
✓ Branch 0 taken 96 times.
✓ Branch 1 taken 205 times.
301 if (is_multi_value) {
3869 96 ctx->add_vcol[j].m_col.prtype |= DATA_MULTI_VALUE;
3870 }
3871
3872 301 ctx->add_vcol[j].m_col.mtype = col_type;
3873
3874 301 ctx->add_vcol[j].m_col.len = col_len;
3875
3876 301 ctx->add_vcol[j].m_col.ind = i - 1;
3877 301 ctx->add_vcol[j].num_base = field->gcol_info->non_virtual_base_columns();
3878 301 ctx->add_vcol_name[j] = field->field_name;
3879 602 ctx->add_vcol[j].base_col = static_cast<dict_col_t **>(mem_heap_alloc(
3880 ctx->heap,
3881
1/2
✓ Branch 0 taken 301 times.
✗ Branch 1 not taken.
301 ctx->add_vcol[j].num_base * sizeof *(ctx->add_vcol[j].base_col)));
3882 301 ctx->add_vcol[j].v_pos =
3883 301 ctx->old_table->n_v_cols - ha_alter_info->virtual_column_drop_count + j;
3884
3885 /* No need to track the list */
3886 301 ctx->add_vcol[j].v_indexes = nullptr;
3887
1/2
✓ Branch 0 taken 301 times.
✗ Branch 1 not taken.
301 innodb_base_col_setup(ctx->old_table, field, &ctx->add_vcol[j]);
3888 301 j++;
3889 }
3890
3891 288 return (false);
3892 }
3893
3894 /** Collect virtual column info for its addition
3895 @param[in] ha_alter_info Data used during in-place alter
3896 @param[in] table MySQL table as it is before the ALTER operation
3897 @retval true Failure
3898 @retval false Success */
3899 96 static bool prepare_inplace_drop_virtual(Alter_inplace_info *ha_alter_info,
3900 const TABLE *table) {
3901 ha_innobase_inplace_ctx *ctx;
3902 96 ulint j = 0;
3903
3904 96 ctx = static_cast<ha_innobase_inplace_ctx *>(ha_alter_info->handler_ctx);
3905
3906 192 ctx->drop_vcol = static_cast<dict_v_col_t *>(mem_heap_alloc(
3907 ctx->heap,
3908 96 ha_alter_info->virtual_column_drop_count * sizeof *ctx->drop_vcol));
3909 192 ctx->drop_vcol_name = static_cast<const char **>(mem_heap_alloc(
3910 ctx->heap,
3911 96 ha_alter_info->virtual_column_drop_count * sizeof *ctx->drop_vcol_name));
3912
3913
2/2
✓ Branch 0 taken 173 times.
✓ Branch 1 taken 95 times.
268 for (const Alter_drop *drop : ha_alter_info->alter_info->drop_list) {
3914 const Field *field;
3915 ulint old_i;
3916
3917
2/2
✓ Branch 0 taken 71 times.
✓ Branch 1 taken 102 times.
173 if (drop->type != Alter_drop::COLUMN) continue;
3918
3919
1/2
✓ Branch 0 taken 487 times.
✗ Branch 1 not taken.
487 for (old_i = 0; table->field[old_i]; old_i++) {
3920 487 const Field *n_field = table->field[old_i];
3921
3/4
✓ Branch 0 taken 487 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 102 times.
✓ Branch 3 taken 385 times.
487 if (!my_strcasecmp(system_charset_info, n_field->field_name,
3922 drop->name)) {
3923 102 break;
3924 }
3925 }
3926 /* SQL-layer already has checked that all columns to be dropped exist. */
3927
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 102 times.
102 ut_ad(table->field[old_i]);
3928 102 field = table->field[old_i];
3929
3930 /*
3931 We don't support simultaneous removal of virtual and stored columns
3932 as in-place operation yet.
3933 */
3934
3/6
✓ Branch 0 taken 102 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 102 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 102 times.
102 ut_ad(field->gcol_info && !field->stored_in_db);
3935
3936 ulint col_len;
3937 ulint is_unsigned;
3938 ulint field_type;
3939 ulint charset_no;
3940
3941
1/2
✓ Branch 0 taken 102 times.
✗ Branch 1 not taken.
102 ulint col_type = get_innobase_type_from_mysql_type(&is_unsigned, field);
3942
3943
1/2
✓ Branch 0 taken 102 times.
✗ Branch 1 not taken.
102 bool is_multi_value = innobase_is_multi_value_fld(field);
3944
3945
2/4
✓ Branch 0 taken 102 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 102 times.
102 if (!field->gcol_info || field->stored_in_db) {
3946 my_error(ER_WRONG_KEY_COLUMN, MYF(0), field->field_name);
3947 1 return (true);
3948 }
3949
3950
2/2
✓ Branch 0 taken 39 times.
✓ Branch 1 taken 63 times.
102 if (is_multi_value) {
3951
1/2
✓ Branch 0 taken 39 times.
✗ Branch 1 not taken.
39 col_len = field->key_length();
3952 } else {
3953
1/2
✓ Branch 0 taken 63 times.
✗ Branch 1 not taken.
63 col_len = field->pack_length();
3954 }
3955
3956
1/2
✓ Branch 0 taken 102 times.
✗ Branch 1 not taken.
102 field_type = (ulint)field->type();
3957
3958
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 100 times.
102 if (!field->is_nullable()) {
3959 2 field_type |= DATA_NOT_NULL;
3960 }
3961
3962
3/4
✓ Branch 0 taken 102 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 90 times.
✓ Branch 3 taken 12 times.
102 if (field->binary()) {
3963 90 field_type |= DATA_BINARY_TYPE;
3964 }
3965
3966
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 98 times.
102 if (is_unsigned) {
3967 4 field_type |= DATA_UNSIGNED;
3968 }
3969
3970
3/4
✓ Branch 0 taken 102 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 36 times.
✓ Branch 3 taken 66 times.
102 if (dtype_is_string_type(col_type)) {
3971
1/2
✓ Branch 0 taken 36 times.
✗ Branch 1 not taken.
36 charset_no = (ulint)field->charset()->number;
3972
3973
3/4
✓ Branch 0 taken 36 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 35 times.
36 DBUG_EXECUTE_IF("ib_alter_add_virtual_fail",
3974 charset_no += MAX_CHAR_COLL_NUM;);
3975
3976
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 35 times.
36 if (charset_no > MAX_CHAR_COLL_NUM) {
3977
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 my_error(ER_WRONG_KEY_COLUMN, MYF(0), field->field_name);
3978 1 return (true);
3979 }
3980 } else {
3981 66 charset_no = 0;
3982 }
3983
3984
7/8
✓ Branch 0 taken 101 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 15 times.
✓ Branch 3 taken 86 times.
✓ Branch 4 taken 4 times.
✓ Branch 5 taken 11 times.
✓ Branch 6 taken 4 times.
✓ Branch 7 taken 97 times.
101 if (field->type() == MYSQL_TYPE_VARCHAR && !is_multi_value) {
3985
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 uint32_t length_bytes = field->get_length_bytes();
3986
3987 4 col_len -= length_bytes;
3988
3989
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
4 if (length_bytes == 2) {
3990 2 field_type |= DATA_LONG_TRUE_VARCHAR;
3991 }
3992 }
3993
3994
1/2
✓ Branch 0 taken 101 times.
✗ Branch 1 not taken.
101 ctx->drop_vcol[j].m_col.prtype = dtype_form_prtype(field_type, charset_no);
3995
3996 101 ctx->drop_vcol[j].m_col.prtype |= DATA_VIRTUAL;
3997
3998 101 ctx->drop_vcol[j].m_col.mtype = col_type;
3999
4000 101 ctx->drop_vcol[j].m_col.len = col_len;
4001
4002 101 ctx->drop_vcol[j].m_col.ind = old_i;
4003
4004 101 ctx->drop_vcol_name[j] = field->field_name;
4005
4006
1/2
✓ Branch 0 taken 101 times.
✗ Branch 1 not taken.
101 dict_v_col_t *v_col = dict_table_get_nth_v_col_mysql(ctx->old_table, old_i);
4007 101 ctx->drop_vcol[j].v_pos = v_col->v_pos;
4008 101 j++;
4009 }
4010
4011 95 return (false);
4012 }
4013
4014 /** Adjust the create index column number from "New table" to
4015 "old InnoDB table" while we are doing dropping virtual column. Since we do
4016 not create separate new table for the dropping/adding virtual columns.
4017 To correctly find the indexed column, we will need to find its col_no
4018 in the "Old Table", not the "New table".
4019 @param[in] ha_alter_info Data used during in-place alter
4020 @param[in] old_table MySQL table as it is before the ALTER operation
4021 @param[in] num_v_dropped number of virtual column dropped
4022 @param[in,out] index_def index definition */
4023 10 static void innodb_v_adjust_idx_col(const Alter_inplace_info *ha_alter_info,
4024 const TABLE *old_table, ulint num_v_dropped,
4025 ddl::Index_defn *index_def) {
4026 List_iterator_fast<Create_field> cf_it(
4027
1/2
✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
10 ha_alter_info->alter_info->create_list);
4028
2/2
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 10 times.
24 for (ulint i = 0; i < index_def->m_n_fields; i++) {
4029 #ifdef UNIV_DEBUG
4030 14 bool col_found = false;
4031 #endif /* UNIV_DEBUG */
4032 14 ulint num_v = 0;
4033
4034 14 auto index_field = &index_def->m_fields[i];
4035
4036 /* Only adjust virtual column col_no, since non-virtual
4037 column position (in non-vcol list) won't change unless
4038 table rebuild */
4039
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 11 times.
14 if (!index_field->m_is_v_col) {
4040 3 continue;
4041 }
4042
4043 11 const Field *field = nullptr;
4044
4045 11 cf_it.rewind();
4046
4047 /* Found the field in the new table */
4048
1/2
✓ Branch 0 taken 31 times.
✗ Branch 1 not taken.
31 while (const Create_field *new_field = cf_it++) {
4049
2/2
✓ Branch 0 taken 19 times.
✓ Branch 1 taken 12 times.
31 if (!new_field->is_virtual_gcol()) {
4050 19 continue;
4051 }
4052
4053 12 field = new_field->field;
4054
4055
2/2
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 1 times.
12 if (num_v == index_field->m_col_no) {
4056 11 break;
4057 }
4058 1 num_v++;
4059 20 }
4060
4061
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
11 if (!field) {
4062 /* this means the field is a newly added field, this
4063 should have been blocked when we drop virtual column
4064 at the same time */
4065 ut_ad(num_v_dropped > 0);
4066 ut_error;
4067 }
4068
4069
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
11 ut_ad(field->is_virtual_gcol());
4070
4071 11 num_v = 0;
4072
4073 /* Look for its position in old table */
4074
1/2
✓ Branch 0 taken 42 times.
✗ Branch 1 not taken.
42 for (uint old_i = 0; old_table->field[old_i]; old_i++) {
4075
2/2
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 31 times.
42 if (old_table->field[old_i] == field) {
4076 /* Found it, adjust its col_no to its position
4077 in old table */
4078 11 index_def->m_fields[i].m_col_no = num_v;
4079 11 ut_d(col_found = true);
4080 11 break;
4081 }
4082
4083
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 19 times.
31 if (old_table->field[old_i]->is_virtual_gcol()) {
4084 12 num_v++;
4085 }
4086 }
4087
4088
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
11 ut_ad(col_found);
4089 }
4090 10 }
4091
4092 /** Replace the table name in filename with the specified one
4093 @param[in] filename original file name
4094 @param[out] new_filename new file name
4095 @param[in] table_name to replace with this table name,
4096 in the format of db/name */
4097 21109 static void replace_table_name(const char *filename, char *new_filename,
4098 const char *table_name) {
4099 21109 const char *slash = strrchr(filename, OS_PATH_SEPARATOR);
4100 21109 size_t len = 0;
4101
4102
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 21109 times.
21109 if (slash == nullptr) {
4103 len = 0;
4104 } else {
4105 21109 len = slash - filename + 1;
4106 }
4107
4108 21109 memcpy(new_filename, filename, len);
4109
4110 21109 slash = strchr(table_name, '/');
4111
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 21109 times.
21109 ut_ad(slash != nullptr);
4112
4113 21109 strcpy(new_filename + len, slash + 1);
4114
4115 21109 len += strlen(slash + 1);
4116
4117 21109 strcpy(new_filename + len, dot_ext[IBD]);
4118 21109 }
4119
4120 /** Update the metadata in prepare phase. This only check if dd::Tablespace
4121 should be removed or(and) created, because to remove and store dd::Tablespace
4122 could fail, so it's better to do it earlier, to prevent a late rollback
4123 @param[in,out] thd MySQL connection
4124 @param[in] old_table Old InnoDB table object
4125 @param[in,out] new_table New InnoDB table object
4126 @param[in] old_dd_tab Old dd::Table or dd::Partition
4127 @return false On success
4128 @retval true On failure */
4129 template <typename Table>
4130 119706 [[nodiscard]] static bool dd_prepare_inplace_alter_table(
4131 THD *thd, const dict_table_t *old_table, dict_table_t *new_table,
4132 const Table *old_dd_tab) {
4133
7/8
✓ Branch 0 taken 59853 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 59839 times.
✓ Branch 3 taken 14 times.
✓ Branch 4 taken 24045 times.
✓ Branch 5 taken 35794 times.
✓ Branch 6 taken 24059 times.
✓ Branch 7 taken 35794 times.
119706 if (new_table->is_temporary() || old_table == new_table) {
4134 /* No need to fill in metadata for temporary tables,
4135 which would not be stored in Global DD */
4136 48118 return false;
4137 }
4138
4139
1/2
✓ Branch 0 taken 35794 times.
✗ Branch 1 not taken.
71588 dd::cache::Dictionary_client *client = dd::get_dd_client(thd);
4140
1/2
✓ Branch 0 taken 35794 times.
✗ Branch 1 not taken.
71588 dd::cache::Dictionary_client::Auto_releaser releaser(client);
4141
4142 71588 uint64_t autoextend_size{};
4143
4144
3/4
✓ Branch 0 taken 35794 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 22558 times.
✓ Branch 3 taken 13236 times.
71588 if (dict_table_is_file_per_table(old_table)) {
4145
2/4
✓ Branch 0 taken 22558 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 22558 times.
✗ Branch 3 not taken.
45116 dd::Object_id old_space_id = dd_first_index(old_dd_tab)->tablespace_id();
4146
4147 /* Copy the autoextend_size attribute value for the tablespace being
4148 dropped. This value will be copied to the new tablespace created later. */
4149
2/4
✓ Branch 0 taken 22558 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 22558 times.
45116 if (dd_get_tablespace_size_option(client, old_space_id, &autoextend_size)) {
4150 return true;
4151 }
4152
4153
2/4
✓ Branch 0 taken 22558 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 22558 times.
45116 if (dd_drop_tablespace(client, old_space_id)) {
4154 return true;
4155 }
4156 }
4157
4158
3/4
✓ Branch 0 taken 35794 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 21109 times.
✓ Branch 3 taken 14685 times.
71588 if (dict_table_is_file_per_table(new_table)) {
4159 /* Replace the table name with the final correct one */
4160
1/2
✓ Branch 0 taken 21109 times.
✗ Branch 1 not taken.
42218 char *path = fil_space_get_first_path(new_table->space);
4161 char filename[FN_REFLEN + 1];
4162
1/2
✓ Branch 0 taken 21109 times.
✗ Branch 1 not taken.
42218 replace_table_name(path, filename, old_table->name.m_name);
4163 42218 ut::free(path);
4164
4165 42218 bool discarded = false;
4166
3/4
✓ Branch 0 taken 21109 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 21003 times.
✓ Branch 3 taken 106 times.
42218 if (dict_table_is_file_per_table(old_table)) {
4167
1/2
✓ Branch 0 taken 21003 times.
✗ Branch 1 not taken.
42006 discarded = dd_is_discarded(*old_dd_tab);
4168 }
4169
4170 dd::Object_id dd_space_id;
4171
4172
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 21109 times.
42218 if (dd_create_implicit_tablespace(client, new_table->space,
4173
1/2
✓ Branch 0 taken 21109 times.
✗ Branch 1 not taken.
42218 old_table->name.m_name, filename,
4174 discarded, dd_space_id)) {
4175 my_error(ER_INTERNAL_ERROR, MYF(0),
4176 " InnoDB can't create tablespace object"
4177 " for ",
4178 new_table->name);
4179 return true;
4180 }
4181
4182 42218 new_table->dd_space_id = dd_space_id;
4183 }
4184
4185 71588 return false;
4186 71588 }
4187
4188 /** Update table level instant metadata in commit phase of INPLACE ALTER
4189 @param[in] table InnoDB table object
4190 @param[in] old_dd_tab old dd::Table
4191 @param[in] new_dd_tab new dd::Table */
4192 22581 static void dd_commit_inplace_update_instant_meta(const dict_table_t *table,
4193 const dd::Table *old_dd_tab,
4194 dd::Table *new_dd_tab) {
4195 /** If table->skip_alter_undo is true during inplace, it is expanded fast
4196 index creation. The inplace ALTERs for that can only be about DROP INDEX
4197 and ADD INDEX and can never be instant operations */
4198
6/6
✓ Branch 0 taken 22558 times.
✓ Branch 1 taken 23 times.
✓ Branch 2 taken 22520 times.
✓ Branch 3 taken 38 times.
✓ Branch 4 taken 22543 times.
✓ Branch 5 taken 38 times.
22581 if (table->skip_alter_undo || !dd_table_has_instant_cols(*old_dd_tab)) {
4199 22543 return;
4200 }
4201
4202
3/6
✓ Branch 0 taken 38 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 38 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 38 times.
38 ut_ad(table->has_instant_cols() || table->has_row_versions());
4203
4204 38 const char *s = dd_table_key_strings[DD_TABLE_INSTANT_COLS];
4205
3/6
✓ Branch 0 taken 38 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 38 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 38 times.
38 if (old_dd_tab->se_private_data().exists(s)) {
4206 ut_ad(table->is_upgraded_instant());
4207 uint32_t value = 0;
4208 old_dd_tab->se_private_data().get(s, &value);
4209 new_dd_tab->se_private_data().set(s, value);
4210 }
4211
4212 /* Copy instant default values of columns if exists */
4213
2/2
✓ Branch 0 taken 171 times.
✓ Branch 1 taken 38 times.
209 for (uint16_t i = 0; i < table->get_n_user_cols(); ++i) {
4214 171 const dict_col_t *col = table->get_col(i);
4215
4216
2/2
✓ Branch 0 taken 118 times.
✓ Branch 1 taken 53 times.
171 if (col->instant_default == nullptr) {
4217 118 continue;
4218 }
4219
4220 dd::Column *dd_col = const_cast<dd::Column *>(
4221 53 dd_find_column(new_dd_tab, table->get_col_name(i)));
4222
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 53 times.
53 ut_ad(dd_col != nullptr);
4223
4224 53 dd_write_default_value(col, dd_col);
4225 }
4226 }
4227
4228 /** Update metadata in commit phase. Note this function should only update
4229 the metadata which would not result in failure
4230 @param[in] old_info Some table information for the old table
4231 @param[in,out] new_table New InnoDB table object
4232 @param[in] old_dd_tab Old dd::Table or dd::Partition
4233 @param[in,out] new_dd_tab New dd::Table or dd::Partition */
4234 template <typename Table>
4235 118242 static void dd_commit_inplace_alter_table(
4236 const alter_table_old_info_t &old_info, dict_table_t *new_table,
4237 const Table *old_dd_tab, Table *new_dd_tab) {
4238
2/2
✓ Branch 0 taken 13 times.
✓ Branch 1 taken 59108 times.
118242 if (new_table->is_temporary()) {
4239 /* No need to fill in metadata for temporary tables,
4240 which would not be stored in Global DD */
4241 26 return;
4242 }
4243
4244 dd::Object_id dd_space_id;
4245
4246
2/2
✓ Branch 0 taken 35587 times.
✓ Branch 1 taken 23521 times.
118216 if (old_info.m_rebuild) {
4247
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 35587 times.
71174 ut_ad(!new_table->has_instant_cols());
4248
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 35587 times.
71174 ut_ad(!new_table->has_row_versions());
4249
4250
2/2
✓ Branch 0 taken 20921 times.
✓ Branch 1 taken 14666 times.
71174 if (dict_table_is_file_per_table(new_table)) {
4251 /* Get the one created in prepare phase */
4252 41842 dd_space_id = new_table->dd_space_id;
4253
2/2
✓ Branch 0 taken 92 times.
✓ Branch 1 taken 14574 times.
29332 } else if (new_table->space == TRX_SYS_SPACE) {
4254 184 dd_space_id = dict_sys_t::s_dd_sys_space_id;
4255 } else {
4256 /* Currently, even if specifying a new TABLESPACE
4257 for partitioned table, existing partitions would not
4258 be moved to new tablespaces. Thus, the old
4259 tablespace id should still be used for new partition */
4260
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 14574 times.
29148 if (dd_table_is_partitioned(new_dd_tab->table())) {
4261 dd_space_id = dd_first_index(old_dd_tab)->tablespace_id();
4262 } else {
4263 29148 dd_space_id = dd_get_space_id(new_dd_tab->table());
4264 }
4265
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 14574 times.
29148 ut_ad(dd_space_id != dd::INVALID_OBJECT_ID);
4266 }
4267 } else {
4268
4/4
✓ Branch 0 taken 384 times.
✓ Branch 1 taken 23137 times.
✓ Branch 2 taken 68 times.
✓ Branch 3 taken 23453 times.
47810 if (old_info.m_fts_doc_id &&
4269
2/2
✓ Branch 0 taken 68 times.
✓ Branch 1 taken 316 times.
768 !dd_find_column(&new_dd_tab->table(), FTS_DOC_ID_COL_NAME)) {
4270 dd::Column *col =
4271 136 dd_add_hidden_column(&new_dd_tab->table(), FTS_DOC_ID_COL_NAME,
4272 FTS_DOC_ID_LEN, dd::enum_column_types::LONGLONG);
4273
4274 136 dd_set_hidden_unique_index(new_dd_tab->table().add_index(),
4275 FTS_DOC_ID_INDEX_NAME, col);
4276 }
4277
4278 /* This can happen only with expanded fast index creation. On the
4279 intermediate table during ALTER COPY, we drop secondary indexes using
4280 inplace alter APIs. The old definition here is old copy of table. Hence we
4281 should use new_dd_tab here for updating the dd::Indexes */
4282
2/2
✓ Branch 0 taken 34 times.
✓ Branch 1 taken 23487 times.
47042 if (new_table->skip_alter_undo) {
4283 68 dd_space_id = dd_first_index(new_dd_tab)->tablespace_id();
4284 } else {
4285 46974 dd_space_id = dd_first_index(old_dd_tab)->tablespace_id();
4286 }
4287
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23521 times.
47042 ut_ad(dd_space_id != dd::INVALID_OBJECT_ID);
4288 }
4289
4290 118216 dd_set_table_options(new_dd_tab, new_table);
4291
4292 118216 new_table->dd_space_id = dd_space_id;
4293
4294 118216 dd_write_table(dd_space_id, new_dd_tab, new_table);
4295
4296 /* If this table is discarded, we need to set this to both dd::Table
4297 and dd::Tablespace. */
4298
2/2
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 59097 times.
118216 if (old_info.m_discarded) {
4299
1/2
✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
22 dd_set_discarded(*new_dd_tab, true);
4300
4301
1/2
✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
22 THD *thd = current_thd;
4302 dd::Object_id dd_space_id =
4303
4/8
✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 11 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 11 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 11 times.
✗ Branch 7 not taken.
22 (*new_dd_tab->indexes()->begin())->tablespace_id();
4304
1/2
✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
22 std::string space_name(new_table->name.m_name);
4305
1/2
✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
22 dict_name::convert_to_space(space_name);
4306
2/4
✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 11 times.
✗ Branch 3 not taken.
22 dd_tablespace_set_state(thd, dd_space_id, space_name,
4307 DD_SPACE_STATE_DISCARDED);
4308 }
4309 }
4310
4311 template <typename Table>
4312 static void dd_commit_inplace_no_change(const Alter_inplace_info *ha_alter_info,
4313 const Table *old_dd_tab,
4314 Table *new_dd_tab, bool ignore_fts) {
4315 if (!ignore_fts) {
4316 dd_add_fts_doc_id_index(new_dd_tab->table(), old_dd_tab->table());
4317 }
4318
4319 dd_copy_private(*new_dd_tab, *old_dd_tab);
4320
4321 if (!dd_table_is_partitioned(new_dd_tab->table()) ||
4322 dd_part_is_first(reinterpret_cast<dd::Partition *>(new_dd_tab))) {
4323 dd_copy_table(ha_alter_info, new_dd_tab->table(), old_dd_tab->table());
4324 }
4325 }
4326
4327 /** Check if a new table's index will exceed the index limit for the table
4328 row format
4329 @param[in] form MySQL table that is being altered
4330 @param[in] max_len max index length allowed
4331 @return true if within limits false otherwise */
4332 37 static bool innobase_check_index_len(const TABLE *form, ulint max_len) {
4333
2/2
✓ Branch 0 taken 22 times.
✓ Branch 1 taken 37 times.
59 for (uint key_num = 0; key_num < form->s->keys; key_num++) {
4334 22 const KEY &key = form->key_info[key_num];
4335
4336
2/2
✓ Branch 0 taken 25 times.
✓ Branch 1 taken 22 times.
47 for (unsigned i = 0; i < key.user_defined_key_parts; i++) {
4337 25 const KEY_PART_INFO *key_part = &key.key_part[i];
4338 25 unsigned prefix_len = 0;
4339
4340
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 25 times.
25 if (key.flags & HA_SPATIAL) {
4341 prefix_len = 0;
4342
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 25 times.
25 } else if (key.flags & HA_FULLTEXT) {
4343 prefix_len = 0;
4344
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 22 times.
25 } else if (key_part->key_part_flag & HA_PART_KEY_SEG) {
4345 /* SPATIAL and FULLTEXT index always are on
4346 full columns. */
4347
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 ut_ad(!(key.flags & (HA_SPATIAL | HA_FULLTEXT)));
4348 3 prefix_len = key_part->length;
4349
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 ut_ad(prefix_len > 0);
4350 } else {
4351 22 prefix_len = 0;
4352 }
4353
4354
2/4
✓ Branch 0 taken 25 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 25 times.
25 if (key_part->length > max_len || prefix_len > max_len) {
4355 return (false);
4356 }
4357 }
4358 }
4359 37 return (true);
4360 }
4361
4362 /** Update internal structures with concurrent writes blocked,
4363 while preparing ALTER TABLE.
4364
4365 @param ha_alter_info Data used during in-place alter
4366 @param altered_table MySQL table that is being altered
4367 @param old_table MySQL table as it is before the ALTER operation
4368 @param old_dd_tab old dd table
4369 @param new_dd_tab new dd table
4370 @param table_name Table name in MySQL
4371 @param flags Table and tablespace flags
4372 @param flags2 Additional table flags
4373 @param fts_doc_id_col The column number of FTS_DOC_ID
4374 @param add_fts_doc_id Flag: add column FTS_DOC_ID?
4375 @param add_fts_doc_id_idx Flag: add index FTS_DOC_ID_INDEX (FTS_DOC_ID)?
4376
4377 @retval true Failure
4378 @retval false Success */
4379 template <typename Table>
4380 88208 [[nodiscard]] static bool prepare_inplace_alter_table_dict(
4381 Alter_inplace_info *ha_alter_info, const TABLE *altered_table,
4382 const TABLE *old_table, const Table *old_dd_tab, Table *new_dd_tab,
4383 const char *table_name, uint32_t flags, uint32_t flags2,
4384 ulint fts_doc_id_col, bool add_fts_doc_id, bool add_fts_doc_id_idx,
4385 row_prebuilt_t *prebuilt) {
4386 88208 bool dict_locked = false;
4387 ulint *add_key_nums; /* MySQL key numbers */
4388 ddl::Index_defn *index_defs; /* index definitions */
4389 dict_table_t *user_table;
4390 88208 dict_index_t *fts_index = nullptr;
4391 dberr_t error;
4392 ulint num_fts_index;
4393 88208 dict_add_v_col_t *add_v = nullptr;
4394 dict_table_t *table;
4395 88208 MDL_ticket *mdl = nullptr;
4396
1/2
✓ Branch 0 taken 44104 times.
✗ Branch 1 not taken.
88208 THD *thd = current_thd;
4397 88208 bool build_fts_common = false;
4398
4399 ha_innobase_inplace_ctx *ctx;
4400 88208 KeyringEncryptionKeyIdInfo keyring_encryption_key_id;
4401 88208 bool none_explicitly_specified = Encryption::none_explicitly_specified(
4402 88208 ha_alter_info->create_info->explicit_encryption,
4403 88208 ha_alter_info->create_info->encrypt_type.str);
4404
4405
1/2
✓ Branch 0 taken 44104 times.
✗ Branch 1 not taken.
88208 DBUG_TRACE;
4406
4407 88208 ctx = static_cast<ha_innobase_inplace_ctx *>(ha_alter_info->handler_ctx);
4408
4409
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 44104 times.
88208 assert((ctx->add_autoinc != ULINT_UNDEFINED) ==
4410 (ctx->sequence.m_max_value > 0));
4411
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 44104 times.
88208 assert(!ctx->num_to_drop_index == !ctx->drop_index);
4412
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 44104 times.
88208 assert(!ctx->num_to_drop_fk == !ctx->drop_fk);
4413
3/4
✓ Branch 0 taken 126 times.
✓ Branch 1 taken 43978 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 126 times.
88208 assert(!add_fts_doc_id || add_fts_doc_id_idx);
4414
3/4
✓ Branch 0 taken 141 times.
✓ Branch 1 taken 43963 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 141 times.
88208 assert(!add_fts_doc_id_idx || innobase_fulltext_exist(altered_table));
4415
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 44104 times.
88208 assert(!ctx->add_cols);
4416
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 44104 times.
88208 assert(!ctx->add_index);
4417
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 44104 times.
88208 assert(!ctx->add_key_numbers);
4418
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 44104 times.
88208 assert(!ctx->num_to_add_index);
4419
4420 88208 user_table = ctx->new_table;
4421
1/2
✓ Branch 0 taken 44104 times.
✗ Branch 1 not taken.
88208 bool is_file_per_table = dict_table_is_file_per_table(user_table);
4422
4423
1/2
✓ Branch 0 taken 44104 times.
✗ Branch 1 not taken.
88208 trx_start_if_not_started_xa(ctx->prebuilt->trx, true, UT_LOCATION_HERE);
4424
4425
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 44095 times.
88208 if (ha_alter_info->handler_flags & Alter_inplace_info::DROP_VIRTUAL_COLUMN) {
4426
3/4
✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 8 times.
18 if (prepare_inplace_drop_virtual(ha_alter_info, old_table)) {
4427 2 return true;
4428 }
4429 }
4430
4431
2/2
✓ Branch 0 taken 168 times.
✓ Branch 1 taken 43935 times.
88206 if (ha_alter_info->handler_flags & Alter_inplace_info::ADD_VIRTUAL_COLUMN) {
4432
3/4
✓ Branch 0 taken 168 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 167 times.
336 if (prepare_inplace_add_virtual(ha_alter_info, altered_table, old_table)) {
4433 2 return true;
4434 }
4435
4436 /* Need information for newly added virtual columns
4437 for create index */
4438
1/2
✓ Branch 0 taken 167 times.
✗ Branch 1 not taken.
334 if (ha_alter_info->handler_flags & Alter_inplace_info::ADD_INDEX) {
4439
2/2
✓ Branch 0 taken 180 times.
✓ Branch 1 taken 167 times.
694 for (ulint i = 0; i < ha_alter_info->virtual_column_add_count; i++) {
4440 /* Set mbminmax for newly added column */
4441 ulint i_mbminlen, i_mbmaxlen;
4442 360 dtype_get_mblen(ctx->add_vcol[i].m_col.mtype,
4443
1/2
✓ Branch 0 taken 180 times.
✗ Branch 1 not taken.
360 ctx->add_vcol[i].m_col.prtype, &i_mbminlen,
4444 &i_mbmaxlen);
4445
4446
1/2
✓ Branch 0 taken 180 times.
✗ Branch 1 not taken.
360 ctx->add_vcol[i].m_col.set_mbminmaxlen(i_mbminlen, i_mbmaxlen);
4447 }
4448 add_v = static_cast<dict_add_v_col_t *>(
4449
1/2
✓ Branch 0 taken 167 times.
✗ Branch 1 not taken.
334 mem_heap_alloc(ctx->heap, sizeof *add_v));
4450 334 add_v->n_v_col = ha_alter_info->virtual_column_add_count;
4451 334 add_v->v_col = ctx->add_vcol;
4452 334 add_v->v_col_name = ctx->add_vcol_name;
4453 }
4454 }
4455
4456
2/2
✓ Branch 0 taken 16727 times.
✓ Branch 1 taken 27375 times.
88204 if ((ha_alter_info->handler_flags &
4457 33454 Alter_inplace_info::CHANGE_CREATE_OPTION) &&
4458
2/2
✓ Branch 0 taken 11350 times.
✓ Branch 1 taken 5377 times.
33454 !(ha_alter_info->create_info->used_fields & HA_CREATE_USED_TABLESPACE) &&
4459 22700 ha_alter_info->create_info
4460
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 11348 times.
22700 ->m_implicit_tablespace_autoextend_size_change) {
4461 /* Update the autoextend_size value in the data dictionary. Do not update
4462 if the table is being moved to a new tablespace. The autoextend_size value
4463 for the new tablespace will be updated later. */
4464
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
4 if (prepare_inplace_change_implicit_tablespace_option(
4465
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
4 ctx->prebuilt->trx->mysql_thd, ha_alter_info, ctx->old_table)) {
4466 return true;
4467 }
4468 }
4469
4470 /* There should be no order change for virtual columns coming in
4471 here */
4472
2/4
✓ Branch 0 taken 44102 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 44102 times.
88204 ut_ad(check_v_col_in_order(old_table, altered_table, ha_alter_info));
4473
4474 88204 ctx->trx = ctx->prebuilt->trx;
4475
4476 /* Create table containing all indexes to be built in this
4477 ALTER TABLE ADD INDEX so that they are in the correct order
4478 in the table. */
4479
4480 88204 ctx->num_to_add_index = ha_alter_info->index_add_count;
4481
4482
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 44102 times.
88204 ut_ad(ctx->prebuilt->trx->mysql_thd != nullptr);
4483
1/2
✓ Branch 0 taken 44102 times.
✗ Branch 1 not taken.
88204 const char *path = thd_innodb_tmpdir(ctx->prebuilt->trx->mysql_thd);
4484
4485 264612 index_defs = innobase_create_key_defs(
4486 ctx->heap, ha_alter_info, altered_table, new_dd_tab,
4487 88204 ctx->num_to_add_index, num_fts_index,
4488
2/4
✓ Branch 0 taken 44102 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 44102 times.
✗ Branch 3 not taken.
88204 row_table_got_default_clust_index(ctx->new_table), fts_doc_id_col,
4489 add_fts_doc_id, add_fts_doc_id_idx, old_table, is_file_per_table);
4490
4491 88204 bool new_clustered = DICT_CLUSTERED & index_defs[0].m_ind_type;
4492
4493
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 44100 times.
88204 if (num_fts_index > 1) {
4494
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
4 my_error(ER_INNODB_FT_LIMIT, MYF(0));
4495 4 goto error_handled;
4496 }
4497
4498
2/2
✓ Branch 0 taken 35874 times.
✓ Branch 1 taken 8226 times.
88200 if (new_clustered) {
4499 /* If max index length is reduced due to row format change
4500 make sure the index can all be accomodated in new row format */
4501 71748 ulint max_len = DICT_MAX_FIELD_LEN_BY_FORMAT_FLAG(flags);
4502
4503
4/4
✓ Branch 0 taken 35663 times.
✓ Branch 1 taken 211 times.
✓ Branch 2 taken 37 times.
✓ Branch 3 taken 35837 times.
71748 if (max_len < DICT_MAX_FIELD_LEN_BY_FORMAT(ctx->old_table)) {
4504
2/4
✓ Branch 0 taken 37 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 37 times.
74 if (!innobase_check_index_len(altered_table, max_len)) {
4505 my_error(ER_INDEX_COLUMN_TOO_LONG, MYF(0), max_len);
4506 goto error_handled;
4507 }
4508 }
4509 }
4510
4511
2/2
✓ Branch 0 taken 42525 times.
✓ Branch 1 taken 1575 times.
88200 if (!ctx->online) {
4512 /* This is not an online operation (LOCK=NONE). */
4513
3/6
✓ Branch 0 taken 42525 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 42525 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 42525 times.
170100 } else if (ctx->add_autoinc == ULINT_UNDEFINED && num_fts_index == 0 &&
4514
2/2
✓ Branch 0 taken 35164 times.
✓ Branch 1 taken 7361 times.
85050 (!innobase_need_rebuild(ha_alter_info, old_table,
4515 70328 is_file_per_table) ||
4516
1/2
✓ Branch 0 taken 35164 times.
✗ Branch 1 not taken.
70328 !innobase_fulltext_exist(altered_table))) {
4517 /* InnoDB can perform an online operation (LOCK=NONE). */
4518 } else {
4519 /* This should have been blocked in
4520 check_if_supported_inplace_alter(). */
4521 my_error(ER_NOT_SUPPORTED_YET, MYF(0),
4522 thd_query_unsafe(ctx->prebuilt->trx->mysql_thd).str);
4523 ut_d(ut_error);
4524 ut_o(goto error_handled);
4525 }
4526
4527 /* The primary index would be rebuilt if a FTS Doc ID
4528 column is to be added, and the primary index definition
4529 is just copied from old table and stored in indexdefs[0] */
4530
3/4
✓ Branch 0 taken 126 times.
✓ Branch 1 taken 43974 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 126 times.
88200 assert(!add_fts_doc_id || new_clustered);
4531
5/6
✓ Branch 0 taken 8349 times.
✓ Branch 1 taken 35751 times.
✓ Branch 2 taken 123 times.
✓ Branch 3 taken 8226 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 44100 times.
88200 assert(
4532 new_clustered ==
4533 (innobase_need_rebuild(ha_alter_info, old_table, is_file_per_table) ||
4534 add_fts_doc_id));
4535
4536 /* Allocate memory for dictionary index definitions */
4537
4538 176400 ctx->add_index = static_cast<dict_index_t **>(mem_heap_alloc(
4539
1/2
✓ Branch 0 taken 44100 times.
✗ Branch 1 not taken.
88200 ctx->heap, ctx->num_to_add_index * sizeof *ctx->add_index));
4540 176400 ctx->add_key_numbers = add_key_nums = static_cast<ulint *>(mem_heap_alloc(
4541
1/2
✓ Branch 0 taken 44100 times.
✗ Branch 1 not taken.
88200 ctx->heap, ctx->num_to_add_index * sizeof *ctx->add_key_numbers));
4542
4543 /* Acquire a lock on the table before creating any indexes. */
4544
2/2
✓ Branch 0 taken 42525 times.
✓ Branch 1 taken 1575 times.
88200 if (ctx->online) {
4545 85050 error = DB_SUCCESS;
4546 } else {
4547 3150 error = ddl::lock_table(ctx->prebuilt->trx, ctx->new_table, LOCK_S);
4548
4549
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1575 times.
3150 if (error != DB_SUCCESS) {
4550 goto error_handling;
4551 }
4552 }
4553
4554 /* Latch the InnoDB data dictionary exclusively so that no deadlocks
4555 or lock waits can happen in it during an index create operation. */
4556
4557
1/2
✓ Branch 0 taken 44100 times.
✗ Branch 1 not taken.
88200 row_mysql_lock_data_dictionary(ctx->prebuilt->trx, UT_LOCATION_HERE);
4558
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 44100 times.
88200 ut_ad(ctx->trx == ctx->prebuilt->trx);
4559 88200 dict_locked = true;
4560
4561 /* Wait for background stats processing to stop using the table that
4562 we are going to alter. We know bg stats will not start using it again
4563 until we are holding the data dict locked and we are holding it here
4564 at least until checking ut_ad(user_table->n_ref_count == 1) below.
4565 XXX what may happen if bg stats opens the table after we
4566 have unlocked data dictionary below? */
4567
1/2
✓ Branch 0 taken 44100 times.
✗ Branch 1 not taken.
88200 dict_stats_wait_bg_to_stop_using_table(user_table, ctx->trx);
4568
4569
1/2
✓ Branch 0 taken 44100 times.
✗ Branch 1 not taken.
88200 online_retry_drop_dict_indexes(ctx->new_table, true);
4570
4571
1/2
✓ Branch 0 taken 44100 times.
✗ Branch 1 not taken.
88200 ut_d(dict_table_check_for_dup_indexes(ctx->new_table, CHECK_ABORTED_OK));
4572
4573 /* If a new clustered index is defined for the table we need
4574 to rebuild the table with a temporary name. */
4575
4576
2/2
✓ Branch 0 taken 35874 times.
✓ Branch 1 taken 8226 times.
88200 if (new_clustered) {
4577 143496 const char *new_table_name = dict_mem_create_temporary_tablename(
4578
1/2
✓ Branch 0 taken 35874 times.
✗ Branch 1 not taken.
71748 ctx->heap, ctx->new_table->name.m_name, ctx->new_table->id);
4579 71748 ulint n_cols = 0;
4580 71748 ulint n_v_cols = 0;
4581 71748 ulint n_m_v_cols = 0;
4582 dtuple_t *add_cols;
4583 71748 space_id_t space_id = 0;
4584 71748 ulint key_id = FIL_DEFAULT_ENCRYPTION_KEY;
4585 71748 fil_encryption_t mode = FIL_ENCRYPTION_DEFAULT;
4586
4587 /* SQL-layer already has checked that we are not dropping any
4588 columns in foreign keys to be kept or making referencing column
4589 in a foreign key with SET NULL action non-nullable. So no need to
4590 check this here. */
4591
4592
2/2
✓ Branch 0 taken 252186 times.
✓ Branch 1 taken 35874 times.
576120 for (uint i = 0; i < altered_table->s->fields; i++) {
4593 504372 const Field *field = altered_table->field[i];
4594
4595
4/4
✓ Branch 0 taken 936 times.
✓ Branch 1 taken 251250 times.
✓ Branch 2 taken 914 times.
✓ Branch 3 taken 22 times.
504372 if (innobase_is_v_fld(field)) {
4596 1828 n_v_cols++;
4597
3/4
✓ Branch 0 taken 914 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 912 times.
1828 if (innobase_is_multi_value_fld(field)) {
4598 4 n_m_v_cols++;
4599 }
4600 } else {
4601 502544 n_cols++;
4602 }
4603 }
4604
4605
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 35874 times.
71748 ut_ad(n_cols + n_v_cols == altered_table->s->fields);
4606
4607
2/2
✓ Branch 0 taken 126 times.
✓ Branch 1 taken 35748 times.
71748 if (add_fts_doc_id) {
4608 252 n_cols++;
4609
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 126 times.
252 assert(flags2 & DICT_TF2_FTS);
4610
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 126 times.
252 assert(add_fts_doc_id_idx);
4611 252 flags2 |=
4612 DICT_TF2_FTS_ADD_DOC_ID | DICT_TF2_FTS_HAS_DOC_ID | DICT_TF2_FTS;
4613 }
4614
4615
3/4
✓ Branch 0 taken 127 times.
✓ Branch 1 taken 35747 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 127 times.
71748 assert(!add_fts_doc_id_idx || (flags2 & DICT_TF2_FTS));
4616
4617 /* Create the table. */
4618
1/2
✓ Branch 0 taken 35874 times.
✗ Branch 1 not taken.
71748 table = dd_table_open_on_name(thd, &mdl, new_table_name, true,
4619 DICT_ERR_IGNORE_NONE);
4620
4621
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 35874 times.
71748 if (table) {
4622 my_error(ER_TABLE_EXISTS_ERROR, MYF(0), new_table_name);
4623 dd_table_close(table, thd, &mdl, true);
4624 goto new_clustered_failed;
4625 }
4626
4627 /* Use the old tablespace unless the tablespace
4628 is changing. */
4629
4/4
✓ Branch 0 taken 13221 times.
✓ Branch 1 taken 22653 times.
✓ Branch 2 taken 13015 times.
✓ Branch 3 taken 22859 times.
98190 if (DICT_TF_HAS_SHARED_SPACE(user_table->flags) &&
4630
1/2
✓ Branch 0 taken 13221 times.
✗ Branch 1 not taken.
26442 (ha_alter_info->create_info->tablespace == nullptr ||
4631
2/2
✓ Branch 0 taken 13015 times.
✓ Branch 1 taken 206 times.
26442 (0 == strcmp(ha_alter_info->create_info->tablespace,
4632 user_table->tablespace)))) {
4633 26030 space_id = user_table->space;
4634
2/2
✓ Branch 0 taken 1682 times.
✓ Branch 1 taken 21177 times.
45718 } else if (tablespace_is_shared_space(ha_alter_info->create_info)) {
4635 space_id =
4636
1/2
✓ Branch 0 taken 1682 times.
✗ Branch 1 not taken.
3364 fil_space_get_id_by_name(ha_alter_info->create_info->tablespace);
4637
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1682 times.
3364 ut_a(space_id != SPACE_UNKNOWN);
4638 }
4639
4640 /* The initial space id 0 may be overridden later if this
4641 table is going to be a file_per_table tablespace. */
4642 71748 ctx->new_table =
4643
1/2
✓ Branch 0 taken 35874 times.
✗ Branch 1 not taken.
71748 dict_mem_table_create(new_table_name, space_id, n_cols + n_v_cols,
4644 n_v_cols, n_m_v_cols, flags, flags2);
4645
4646 /* TODO: Fix this problematic assignment */
4647
1/2
✓ Branch 0 taken 35874 times.
✗ Branch 1 not taken.
71748 ctx->new_table->dd_space_id = new_dd_tab->tablespace_id();
4648
4649 /* The rebuilt indexed_table will use the renamed
4650 column names. */
4651 71748 ctx->col_names = nullptr;
4652
4653
2/2
✓ Branch 0 taken 152 times.
✓ Branch 1 taken 35722 times.
71748 if (DICT_TF_HAS_DATA_DIR(flags)) {
4654 608 ctx->new_table->data_dir_path =
4655
1/2
✓ Branch 0 taken 152 times.
✗ Branch 1 not taken.
304 mem_heap_strdup(ctx->new_table->heap, user_table->data_dir_path);
4656 }
4657
4658
2/2
✓ Branch 0 taken 252186 times.
✓ Branch 1 taken 35874 times.
576120 for (uint i = 0; i < altered_table->s->fields; i++) {
4659 504372 const Field *field = altered_table->field[i];
4660 ulint is_unsigned;
4661
1/2
✓ Branch 0 taken 252186 times.
✗ Branch 1 not taken.
504372 ulint field_type = (ulint)field->type();
4662
1/2
✓ Branch 0 taken 252186 times.
✗ Branch 1 not taken.
504372 ulint col_type = get_innobase_type_from_mysql_type(&is_unsigned, field);
4663 ulint charset_no;
4664 ulint col_len;
4665
4/4
✓ Branch 0 taken 936 times.
✓ Branch 1 taken 251250 times.
✓ Branch 2 taken 914 times.
✓ Branch 3 taken 22 times.
504372 bool is_virtual = innobase_is_v_fld(field);
4666
1/2
✓ Branch 0 taken 252186 times.
✗ Branch 1 not taken.
504372 bool is_multi_value = innobase_is_multi_value_fld(field);
4667
4668 /* we assume in dtype_form_prtype() that this
4669 fits in two bytes */
4670
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 252186 times.
504372 ut_a(field_type <= MAX_CHAR_COLL_NUM);
4671
4672
2/2
✓ Branch 0 taken 166747 times.
✓ Branch 1 taken 85439 times.
504372 if (!field->is_nullable()) {
4673 333494 field_type |= DATA_NOT_NULL;
4674 }
4675
4676
3/4
✓ Branch 0 taken 252186 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 107017 times.
✓ Branch 3 taken 145169 times.
504372 if (field->binary()) {
4677 214034 field_type |= DATA_BINARY_TYPE;
4678 }
4679
4680
2/2
✓ Branch 0 taken 88546 times.
✓ Branch 1 taken 163640 times.
504372 if (is_unsigned) {
4681 177092 field_type |= DATA_UNSIGNED;
4682 }
4683
4684
3/4
✓ Branch 0 taken 252186 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 106356 times.
✓ Branch 3 taken 145830 times.
504372 if (dtype_is_string_type(col_type)) {
4685
1/2
✓ Branch 0 taken 106356 times.
✗ Branch 1 not taken.
212712 charset_no = (ulint)field->charset()->number;
4686
4687
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 106356 times.
212712 if (charset_no > MAX_CHAR_COLL_NUM) {
4688 dict_mem_table_free(ctx->new_table);
4689 my_error(ER_WRONG_KEY_COLUMN, MYF(0), field->field_name);
4690 goto new_clustered_failed;
4691 }
4692 } else {
4693 291660 charset_no = 0;
4694 }
4695
4696
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 252184 times.
504372 if (is_multi_value) {
4697
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
4 col_len = field->key_length();
4698 } else {
4699
1/2
✓ Branch 0 taken 252184 times.
✗ Branch 1 not taken.
504368 col_len = field->pack_length();
4700 }
4701
4702 /* The MySQL pack length contains 1 or 2 bytes
4703 length field for a true VARCHAR. Let us
4704 subtract that, so that the InnoDB column
4705 length in the InnoDB data dictionary is the
4706 real maximum byte length of the actual data. */
4707
4708
7/8
✓ Branch 0 taken 252186 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 14224 times.
✓ Branch 3 taken 237962 times.
✓ Branch 4 taken 14222 times.
✓ Branch 5 taken 2 times.
✓ Branch 6 taken 14222 times.
✓ Branch 7 taken 237964 times.
504372 if (field->type() == MYSQL_TYPE_VARCHAR && !is_multi_value) {
4709
1/2
✓ Branch 0 taken 14222 times.
✗ Branch 1 not taken.
28444 uint32_t length_bytes = field->get_length_bytes();
4710
4711 28444 col_len -= length_bytes;
4712
4713
2/2
✓ Branch 0 taken 8226 times.
✓ Branch 1 taken 5996 times.
28444 if (length_bytes == 2) {
4714 16452 field_type |= DATA_LONG_TRUE_VARCHAR;
4715 }
4716 }
4717
4718
2/2
✓ Branch 0 taken 36 times.
✓ Branch 1 taken 252150 times.
504372 if (field->column_format() == COLUMN_FORMAT_TYPE_COMPRESSED)
4719 72 field_type |= DATA_COMPRESSED;
4720
4721
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 252186 times.
504372 if (col_type == DATA_POINT) {
4722 /* DATA_POINT should be of fixed length,
4723 instead of the pack_length(blob length). */
4724 col_len = DATA_POINT_LEN;
4725 }
4726
4727
2/4
✓ Branch 0 taken 252186 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 252186 times.
504372 if (dict_col_name_is_reserved(field->field_name)) {
4728 dict_mem_table_free(ctx->new_table);
4729 my_error(ER_WRONG_COLUMN_NAME, MYF(0), field->field_name);
4730 goto new_clustered_failed;
4731 }
4732
4733
2/2
✓ Branch 0 taken 914 times.
✓ Branch 1 taken 251272 times.
504372 if (is_virtual) {
4734 1828 field_type |= DATA_VIRTUAL;
4735
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 912 times.
1828 if (is_multi_value) {
4736 4 field_type |= DATA_MULTI_VALUE;
4737 }
4738
1/2
✓ Branch 0 taken 914 times.
✗ Branch 1 not taken.
1828 dict_mem_table_add_v_col(
4739
1/2
✓ Branch 0 taken 914 times.
✗ Branch 1 not taken.
1828 ctx->new_table, ctx->heap, field->field_name, col_type,
4740 dtype_form_prtype(field_type, charset_no), col_len, i,
4741 1828 field->gcol_info->non_virtual_base_columns(),
4742
1/2
✓ Branch 0 taken 914 times.
✗ Branch 1 not taken.
1828 !field->is_hidden_by_system());
4743 } else {
4744 1005088 dict_mem_table_add_col(
4745
1/2
✓ Branch 0 taken 251272 times.
✗ Branch 1 not taken.
502544 ctx->new_table, ctx->heap, field->field_name, col_type,
4746 dtype_form_prtype(field_type, charset_no), col_len,
4747
2/4
✓ Branch 0 taken 251272 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 251272 times.
✗ Branch 3 not taken.
502544 !field->is_hidden_by_system(), UINT32_UNDEFINED, UINT8_UNDEFINED,
4748 UINT8_UNDEFINED);
4749 }
4750 }
4751
4752
2/2
✓ Branch 0 taken 900 times.
✓ Branch 1 taken 34974 times.
71748 if (n_v_cols) {
4753 1800 ulint z = 0;
4754
2/2
✓ Branch 0 taken 5338 times.
✓ Branch 1 taken 900 times.
12476 for (uint i = 0; i < altered_table->s->fields; i++) {
4755 dict_v_col_t *v_col;
4756 10676 const Field *field = altered_table->field[i];
4757
4758
4/4
✓ Branch 0 taken 936 times.
✓ Branch 1 taken 4402 times.
✓ Branch 2 taken 22 times.
✓ Branch 3 taken 914 times.
10676 if (!innobase_is_v_fld(field)) {
4759 8848 continue;
4760 }
4761
1/2
✓ Branch 0 taken 914 times.
✗ Branch 1 not taken.
1828 v_col = dict_table_get_nth_v_col(ctx->new_table, z);
4762 1828 z++;
4763
1/2
✓ Branch 0 taken 914 times.
✗ Branch 1 not taken.
1828 innodb_base_col_setup(ctx->new_table, field, v_col);
4764 }
4765 }
4766
4767 /* Populate row version and column counts for new table */
4768 71748 ctx->new_table->current_row_version = 0;
4769 71748 ctx->new_table->initial_col_count = altered_table->s->fields - n_v_cols;
4770 71748 ctx->new_table->current_col_count = ctx->new_table->initial_col_count;
4771 71748 ctx->new_table->total_col_count = ctx->new_table->initial_col_count;
4772
4773
2/2
✓ Branch 0 taken 126 times.
✓ Branch 1 taken 35748 times.
71748 if (add_fts_doc_id) {
4774
1/2
✓ Branch 0 taken 126 times.
✗ Branch 1 not taken.
252 fts_add_doc_id_column(ctx->new_table, ctx->heap);
4775 252 ctx->new_table->fts->doc_col = fts_doc_id_col;
4776
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 126 times.
252 ut_ad(fts_doc_id_col == altered_table->s->fields - n_v_cols);
4777
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 35747 times.
71496 } else if (ctx->new_table->fts) {
4778 2 ctx->new_table->fts->doc_col = fts_doc_id_col;
4779 }
4780
4781 const char *compression;
4782
4783 71748 compression = ha_alter_info->create_info->compress.str;
4784
4785
2/4
✓ Branch 0 taken 35874 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 35874 times.
71748 if (Compression::validate(compression) != DB_SUCCESS) {
4786 compression = nullptr;
4787 }
4788
4789 const char *encrypt;
4790 71748 encrypt = ha_alter_info->create_info->encrypt_type.str;
4791 71748 key_id = ha_alter_info->create_info->encryption_key_id;
4792
4793 // re-encrypting, check that key used to encrypt table is present
4794
2/2
✓ Branch 0 taken 688 times.
✓ Branch 1 taken 35186 times.
71748 if (DICT_TF2_FLAG_IS_SET(ctx->old_table,
4795 DICT_TF2_ENCRYPTION_FILE_PER_TABLE)) {
4796
2/2
✓ Branch 0 taken 686 times.
✓ Branch 1 taken 2 times.
1376 if (Encryption::is_master_key_encryption(
4797 1376 old_table->s->encrypt_type.str)) {
4798 // re-encrypting from master key encryption
4799 /* Check if keyring is ready. */
4800 1372 byte *master_key = NULL;
4801 uint32_t master_key_id;
4802
4803 1372 Encryption::get_master_key(&master_key_id, &master_key);
4804
4805
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 686 times.
1372 if (master_key == NULL) {
4806 dict_mem_table_free(ctx->new_table);
4807 my_error(ER_CANNOT_FIND_KEY_IN_KEYRING, MYF(0));
4808 goto new_clustered_failed;
4809 } else {
4810
1/2
✓ Branch 0 taken 686 times.
✗ Branch 1 not taken.
1372 my_free(master_key);
4811 }
4812 }
4813 }
4814
4815
2/2
✓ Branch 0 taken 13568 times.
✓ Branch 1 taken 22306 times.
71748 if (none_explicitly_specified)
4816 27136 mode = FIL_ENCRYPTION_OFF;
4817 44612 else if (Encryption::is_keyring(encrypt) ||
4818
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 22306 times.
44612 (srv_default_table_encryption ==
4819 DEFAULT_TABLE_ENC_ONLINE_TO_KEYRING &&
4820 !none_explicitly_specified &&
4821
2/6
✓ Branch 0 taken 22306 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 22306 times.
89224 !Encryption::is_master_key_encryption(encrypt)) ||
4822
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 22306 times.
44612 ha_alter_info->create_info->was_encryption_key_id_set) {
4823 mode = Encryption::is_keyring(encrypt) ? FIL_ENCRYPTION_ON
4824 : FIL_ENCRYPTION_DEFAULT;
4825 uint tablespace_key_version;
4826 byte *tablespace_key;
4827
4828 // TODO: Add checking for error returned from keyring function, not only
4829 // checking if tablespace is null
4830 Encryption::get_latest_key_or_create(
4831 key_id, server_uuid, &tablespace_key_version, &tablespace_key);
4832 if (tablespace_key == NULL) {
4833 dict_mem_table_free(ctx->new_table);
4834 my_printf_error(
4835 ER_ILLEGAL_HA_CREATE_OPTION,
4836 "Seems that keyring is down. It is not possible to encrypt table"
4837 " without keyring. Please install a keyring and try again.",
4838 MYF(0));
4839 goto new_clustered_failed;
4840 } else {
4841 my_free(tablespace_key);
4842 }
4843
4844 if (mode == FIL_ENCRYPTION_ON ||
4845 (mode == FIL_ENCRYPTION_DEFAULT &&
4846 srv_default_table_encryption ==
4847 DEFAULT_TABLE_ENC_ONLINE_TO_KEYRING)) {
4848 DICT_TF2_FLAG_SET(ctx->new_table, DICT_TF2_ENCRYPTION_FILE_PER_TABLE);
4849 }
4850 94532 } else if (!(ctx->new_table->flags2 & DICT_TF2_USE_FILE_PER_TABLE) &&
4851
3/4
✓ Branch 0 taken 2654 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 558 times.
✓ Branch 3 taken 2096 times.
10616 ha_alter_info->create_info->encrypt_type.length > 0 &&
4852
3/4
✓ Branch 0 taken 2654 times.
✓ Branch 1 taken 19652 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 22306 times.
55228 Encryption::is_master_key_encryption(encrypt) &&
4853
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 558 times.
1116 !DICT_TF2_FLAG_IS_SET(ctx->old_table,
4854 DICT_TF2_ENCRYPTION_FILE_PER_TABLE)) {
4855 dict_mem_table_free(ctx->new_table);
4856 my_error(ER_TABLESPACE_CANNOT_ENCRYPT, MYF(0));
4857 goto new_clustered_failed;
4858
2/2
✓ Branch 0 taken 686 times.
✓ Branch 1 taken 21620 times.
44612 } else if (Encryption::is_master_key_encryption(encrypt)) {
4859 /* Set the encryption flag. */
4860
4861 /* Check if keyring is ready. */
4862
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 686 times.
1372 if (!Encryption::check_keyring()) {
4863 dict_mem_table_free(ctx->new_table);
4864 my_error(ER_CANNOT_FIND_KEY_IN_KEYRING, MYF(0));
4865 goto new_clustered_failed;
4866 } else {
4867 /* This flag will be used to set encryption
4868 option for file-per-table tablespace. */
4869 1372 DICT_TF2_FLAG_SET(ctx->new_table, DICT_TF2_ENCRYPTION_FILE_PER_TABLE);
4870 }
4871 }
4872
4873
1/2
✓ Branch 0 taken 35874 times.
✗ Branch 1 not taken.
71748 dict_sys_mutex_exit();
4874
4875 71748 keyring_encryption_key_id.was_encryption_key_id_set =
4876 71748 ha_alter_info->create_info->was_encryption_key_id_set;
4877 71748 keyring_encryption_key_id.id = key_id;
4878
4879 143466 error = row_create_table_for_mysql(ctx->new_table, compression,
4880
1/2
✓ Branch 0 taken 35859 times.
✗ Branch 1 not taken.
71748 ha_alter_info->create_info, ctx->trx, nullptr,
4881 mode, keyring_encryption_key_id);
4882
4883
1/2
✓ Branch 0 taken 35859 times.
✗ Branch 1 not taken.
71718 dict_sys_mutex_enter();
4884
4885
2/7
✓ Branch 0 taken 35854 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 5 times.
71718 switch (error) {
4886 dict_table_t *temp_table;
4887 71708 case DB_SUCCESS:
4888 /* To bump up the table ref count and move it
4889 to LRU list if it's not temporary table */
4890
2/4
✓ Branch 0 taken 35854 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 35854 times.
71708 ut_ad(dict_sys_mutex_own());
4891
3/6
✓ Branch 0 taken 35854 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 35854 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 35854 times.
✗ Branch 5 not taken.
143416 if (!ctx->new_table->is_temporary() &&
4892
1/2
✓ Branch 0 taken 35854 times.
✗ Branch 1 not taken.
71708 !ctx->new_table->explicitly_non_lru) {
4893
1/2
✓ Branch 0 taken 35854 times.
✗ Branch 1 not taken.
71708 dict_table_allow_eviction(ctx->new_table);
4894 }
4895
2/2
✓ Branch 0 taken 35727 times.
✓ Branch 1 taken 127 times.
71708 if ((ctx->new_table->flags2 &
4896 71454 (DICT_TF2_FTS | DICT_TF2_FTS_ADD_DOC_ID)) ||
4897
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 35727 times.
71454 ctx->new_table->fts != nullptr) {
4898
1/2
✓ Branch 0 taken 127 times.
✗ Branch 1 not taken.
254 fts_freeze_aux_tables(ctx->new_table);
4899 }
4900 temp_table =
4901
1/2
✓ Branch 0 taken 35854 times.
✗ Branch 1 not taken.
71708 dd_table_open_on_name_in_mem(ctx->new_table->name.m_name, true);
4902
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 35854 times.
71708 ut_a(ctx->new_table == temp_table);
4903 /* n_ref_count must be 1, because purge cannot
4904 be executing on this very table as we are
4905 holding MDL lock. */
4906
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 35854 times.
71708 assert(ctx->new_table->get_ref_count() == 1);
4907 71708 break;
4908 case DB_TABLESPACE_EXISTS:
4909 my_error(ER_TABLESPACE_EXISTS, MYF(0), new_table_name);
4910 goto new_clustered_failed;
4911 case DB_DUPLICATE_KEY:
4912 my_error(HA_ERR_TABLE_EXIST, MYF(0), altered_table->s->table_name.str);
4913 goto new_clustered_failed;
4914 case DB_UNSUPPORTED:
4915 my_error(ER_UNSUPPORTED_EXTENSION, MYF(0), new_table_name);
4916 goto new_clustered_failed;
4917 case DB_IO_NO_PUNCH_HOLE_FS:
4918 my_error(ER_INNODB_COMPRESSION_FAILURE, MYF(0),
4919 "Punch hole not supported by the filesystem or the tablespace "
4920 "page size is not large enough.");
4921 goto new_clustered_failed;
4922 case DB_IO_NO_PUNCH_HOLE_TABLESPACE:
4923 my_error(ER_INNODB_COMPRESSION_FAILURE, MYF(0),
4924 "Page Compression is not supported for this tablespace");
4925 goto new_clustered_failed;
4926 10 default:
4927
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
10 my_error_innodb(error, table_name, flags);
4928 10 new_clustered_failed:
4929
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
10 ut_ad(user_table->get_ref_count() == 1);
4930
4931 10 goto err_exit;
4932 }
4933
4934
2/2
✓ Branch 0 taken 9204 times.
✓ Branch 1 taken 26650 times.
71708 if (ha_alter_info->handler_flags & Alter_inplace_info::ADD_COLUMN) {
4935 add_cols =
4936
2/4
✓ Branch 0 taken 9204 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 9204 times.
✗ Branch 3 not taken.
18408 dtuple_create_with_vcol(ctx->heap, ctx->new_table->get_n_cols(),
4937
1/2
✓ Branch 0 taken 9204 times.
✗ Branch 1 not taken.
18408 dict_table_get_n_v_cols(ctx->new_table));
4938
4939
1/2
✓ Branch 0 taken 9204 times.
✗ Branch 1 not taken.
18408 dict_table_copy_types(add_cols, ctx->new_table);
4940 } else {
4941 53300 add_cols = nullptr;
4942 }
4943
4944 143416 ctx->col_map = innobase_build_col_map(ha_alter_info, altered_table,
4945
1/2
✓ Branch 0 taken 35854 times.
✗ Branch 1 not taken.
71708 old_table, ctx->new_table, user_table,
4946 add_cols, ctx->heap, prebuilt);
4947 71708 ctx->add_cols = add_cols;
4948 } else {
4949
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8226 times.
16452 assert(
4950 !innobase_need_rebuild(ha_alter_info, old_table, is_file_per_table));
4951
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8226 times.
16452 assert(old_table->s->primary_key == altered_table->s->primary_key);
4952
4953
3/4
✓ Branch 0 taken 8226 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18047 times.
✓ Branch 3 taken 8221 times.
52536 for (dict_index_t *index = user_table->first_index(); index != nullptr;
4954
1/2
✓ Branch 0 taken 18042 times.
✗ Branch 1 not taken.
36084 index = index->next()) {
4955
7/8
✓ Branch 0 taken 17856 times.
✓ Branch 1 taken 191 times.
✓ Branch 2 taken 17856 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 5 times.
✓ Branch 5 taken 17851 times.
✓ Branch 6 taken 5 times.
✓ Branch 7 taken 18042 times.
36094 if (!index->to_be_dropped && index->is_corrupted()) {
4956
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
10 my_error(ER_CHECK_NO_SUCH_TABLE, MYF(0));
4957 10 goto error_handled;
4958 }
4959 }
4960
4961
6/6
✓ Branch 0 taken 7918 times.
✓ Branch 1 taken 303 times.
✓ Branch 2 taken 29 times.
✓ Branch 3 taken 7889 times.
✓ Branch 4 taken 29 times.
✓ Branch 5 taken 8192 times.
16442 if (!ctx->new_table->fts && innobase_fulltext_exist(altered_table)) {
4962
1/2
✓ Branch 0 taken 29 times.
✗ Branch 1 not taken.
58 ctx->new_table->fts = fts_create(ctx->new_table);
4963 58 ctx->new_table->fts->doc_col = fts_doc_id_col;
4964 }
4965
4966 /* Check if we need to update mtypes of legacy GIS columns.
4967 This check is only needed when we don't have to rebuild
4968 the table, since rebuild would update all mtypes for GIS
4969 columns */
4970
1/2
✓ Branch 0 taken 8221 times.
✗ Branch 1 not taken.
16442 error = innobase_check_gis_columns(ha_alter_info, ctx->new_table);
4971
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8221 times.
16442 if (error != DB_SUCCESS) {
4972 ut_ad(error == DB_ERROR);
4973 error = DB_UNSUPPORTED;
4974 goto error_handling;
4975 }
4976 }
4977
4978
2/4
✓ Branch 0 taken 44075 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 44075 times.
88150 ut_ad(!dict_table_is_compressed_temporary(ctx->new_table));
4979
4980 /* Assign table_id, so that no table id of
4981 fts_create_index_tables() will be written to the undo logs. */
4982
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 44075 times.
88150 assert(ctx->new_table->id != 0);
4983
4984 /* Create the indexes and load into dictionary. */
4985
4986
2/2
✓ Branch 0 taken 55319 times.
✓ Branch 1 taken 43977 times.
198592 for (ulint a = 0; a < ctx->num_to_add_index; a++) {
4987
2/2
✓ Branch 0 taken 319 times.
✓ Branch 1 taken 55000 times.
110638 if (index_defs[a].m_ind_type & DICT_VIRTUAL &&
4988
3/4
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 309 times.
✓ Branch 2 taken 10 times.
✗ Branch 3 not taken.
638 ha_alter_info->virtual_column_drop_count > 0 && !new_clustered) {
4989 20 innodb_v_adjust_idx_col(ha_alter_info, old_table,
4990 20 ha_alter_info->virtual_column_drop_count,
4991
1/2
✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
20 &index_defs[a]);
4992 }
4993
4994 221120 ctx->add_index[a] =
4995 110638 ddl::create_index(ctx->trx, ctx->new_table, &index_defs[a], add_v);
4996
4997 110482 add_key_nums[a] = index_defs[a].m_key_number;
4998
4999
2/2
✓ Branch 0 taken 19 times.
✓ Branch 1 taken 55222 times.
110482 if (!ctx->add_index[a]) {
5000 38 error = ctx->trx->error_state;
5001
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 19 times.
38 assert(error != DB_SUCCESS);
5002 38 goto error_handling;
5003 }
5004
5005
2/4
✓ Branch 0 taken 55222 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 55222 times.
110444 assert(ctx->add_index[a]->is_committed() == new_clustered);
5006
5007
2/2
✓ Branch 0 taken 429 times.
✓ Branch 1 taken 54793 times.
110444 if (ctx->add_index[a]->type & DICT_FTS) {
5008
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 429 times.
858 assert(num_fts_index);
5009
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 429 times.
858 assert(!fts_index);
5010
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 429 times.
858 assert(ctx->add_index[a]->type == DICT_FTS);
5011 858 fts_index = ctx->add_index[a];
5012 }
5013
5014 /* If only online ALTER TABLE operations have been
5015 requested, allocate a modification log. If the table
5016 will be locked anyway, the modification
5017 log is unnecessary. When rebuilding the table
5018 (new_clustered), we will allocate the log for the
5019 clustered index of the old table, later. */
5020
9/10
✓ Branch 0 taken 8343 times.
✓ Branch 1 taken 46879 times.
✓ Branch 2 taken 7436 times.
✓ Branch 3 taken 907 times.
✓ Branch 4 taken 7432 times.
✓ Branch 5 taken 4 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 7432 times.
✓ Branch 8 taken 47790 times.
✓ Branch 9 taken 7432 times.
125308 if (new_clustered || !ctx->online || user_table->ibd_file_missing ||
5021 14864 dict_table_is_discarded(user_table)) {
5022 /* No need to allocate a modification log. */
5023
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 47790 times.
95580 ut_ad(!ctx->add_index[a]->online_log);
5024
1/2
✓ Branch 0 taken 7432 times.
✗ Branch 1 not taken.
14864 } else if (ctx->add_index[a]->type & DICT_FTS) {
5025 /* Fulltext indexes are not covered
5026 by a modification log. */
5027 } else {
5028
3/4
✓ Branch 0 taken 7432 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 7431 times.
14864 DBUG_EXECUTE_IF("innodb_OOM_prepare_inplace_alter",
5029 error = DB_OUT_OF_MEMORY;
5030 goto error_handling;);
5031
1/2
✓ Branch 0 taken 7431 times.
✗ Branch 1 not taken.
14862 rw_lock_x_lock(&ctx->add_index[a]->lock, UT_LOCATION_HERE);
5032
1/2
✓ Branch 0 taken 7431 times.
✗ Branch 1 not taken.
14862 bool ok = row_log_allocate(ctx->add_index[a], nullptr, true, nullptr,
5033 nullptr, path);
5034
1/2
✓ Branch 0 taken 7431 times.
✗ Branch 1 not taken.
14862 rw_lock_x_unlock(&ctx->add_index[a]->lock);
5035
5036
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 7431 times.
14862 if (!ok) {
5037 error = DB_OUT_OF_MEMORY;
5038 goto error_handling;
5039 }
5040 }
5041 }
5042
5043
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 43977 times.
87954 ut_ad(new_clustered == ctx->need_rebuild());
5044
5045
3/4
✓ Branch 0 taken 43977 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 43976 times.
87954 DBUG_EXECUTE_IF("innodb_OOM_prepare_inplace_alter", error = DB_OUT_OF_MEMORY;
5046 goto error_handling;);
5047
5048
2/2
✓ Branch 0 taken 35798 times.
✓ Branch 1 taken 8178 times.
87952 if (new_clustered) {
5049
1/2
✓ Branch 0 taken 35798 times.
✗ Branch 1 not taken.
71596 dict_index_t *clust_index = user_table->first_index();
5050
1/2
✓ Branch 0 taken 35798 times.
✗ Branch 1 not taken.
71596 dict_index_t *new_clust_index = ctx->new_table->first_index();
5051 71596 ctx->skip_pk_sort =
5052
1/2
✓ Branch 0 taken 35798 times.
✗ Branch 1 not taken.
71596 innobase_pk_order_preserved(ctx->col_map, clust_index, new_clust_index);
5053
5054
4/6
✓ Branch 0 taken 35798 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 44 times.
✓ Branch 3 taken 35754 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 44 times.
71596 DBUG_EXECUTE_IF("innodb_alter_table_pk_assert_no_sort",
5055 assert(ctx->skip_pk_sort););
5056
5057
2/2
✓ Branch 0 taken 35088 times.
✓ Branch 1 taken 710 times.
71596 if (ctx->online) {
5058 /* Allocate a log for online table rebuild. */
5059
1/2
✓ Branch 0 taken 35088 times.
✗ Branch 1 not taken.
70176 rw_lock_x_lock(&clust_index->lock, UT_LOCATION_HERE);
5060 140352 bool ok = row_log_allocate(
5061 clust_index, ctx->new_table,
5062
1/2
✓ Branch 0 taken 35088 times.
✗ Branch 1 not taken.
70176 !(ha_alter_info->handler_flags & Alter_inplace_info::ADD_PK_INDEX),
5063 ctx->add_cols, ctx->col_map, path);
5064
1/2
✓ Branch 0 taken 35088 times.
✗ Branch 1 not taken.
70176 rw_lock_x_unlock(&clust_index->lock);
5065
5066
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 35088 times.
70176 if (!ok) {
5067 error = DB_OUT_OF_MEMORY;
5068 goto error_handling;
5069 }
5070 }
5071 }
5072
5073
2/2
✓ Branch 0 taken 42407 times.
✓ Branch 1 taken 1569 times.
87952 if (ctx->online) {
5074 /* Assign a consistent read view for the index build scan. */
5075
1/2
✓ Branch 0 taken 42407 times.
✗ Branch 1 not taken.
84814 trx_assign_read_view(ctx->prebuilt->trx);
5076 }
5077
5078
2/2
✓ Branch 0 taken 429 times.
✓ Branch 1 taken 43547 times.
87952 if (fts_index) {
5079 /* Ensure that the dictionary operation mode will
5080 not change while creating the auxiliary tables. */
5081 #ifdef UNIV_DEBUG
5082
1/2
✓ Branch 0 taken 429 times.
✗ Branch 1 not taken.
858 trx_dict_op_t op = trx_get_dict_operation(ctx->trx);
5083 #endif
5084
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 429 times.
858 ut_ad(ctx->trx->dict_operation_lock_mode == RW_X_LATCH);
5085
2/4
✓ Branch 0 taken 429 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 429 times.
858 ut_ad(dict_sys_mutex_own());
5086
2/4
✓ Branch 0 taken 429 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 429 times.
858 ut_ad(rw_lock_own(dict_operation_lock, RW_LOCK_X));
5087
5088 858 DICT_TF2_FLAG_SET(ctx->new_table, DICT_TF2_FTS);
5089
2/2
✓ Branch 0 taken 127 times.
✓ Branch 1 taken 302 times.
858 if (new_clustered) {
5090 /* For !new_clustered, this will be set at
5091 commit_cache_norebuild(). */
5092 508 ctx->new_table->fts_doc_id_index =
5093
1/2
✓ Branch 0 taken 127 times.
✗ Branch 1 not taken.
254 dict_table_get_index_on_name(ctx->new_table, FTS_DOC_ID_INDEX_NAME);
5094
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 127 times.
254 assert(ctx->new_table->fts_doc_id_index != nullptr);
5095 }
5096
5097 /* This function will commit the transaction and reset
5098 the trx_t::dict_operation flag on success. */
5099
5100
1/2
✓ Branch 0 taken 429 times.
✗ Branch 1 not taken.
858 dict_sys_mutex_exit();
5101
1/2
✓ Branch 0 taken 429 times.
✗ Branch 1 not taken.
858 error = fts_create_index_tables(ctx->trx, fts_index);
5102
1/2
✓ Branch 0 taken 429 times.
✗ Branch 1 not taken.
858 dict_sys_mutex_enter();
5103
5104
2/4
✓ Branch 0 taken 429 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 429 times.
858 DBUG_EXECUTE_IF("innodb_test_fail_after_fts_index_table",
5105 error = DB_LOCK_WAIT_TIMEOUT;
5106 goto error_handling;);
5107
5108
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 425 times.
858 if (error != DB_SUCCESS) {
5109 8 goto error_handling;
5110 }
5111
5112
5/6
✓ Branch 0 taken 425 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 194 times.
✓ Branch 3 taken 231 times.
✓ Branch 4 taken 194 times.
✓ Branch 5 taken 231 times.
1700 if (!ctx->new_table->fts ||
5113 850 ib_vector_size(ctx->new_table->fts->indexes) == 0) {
5114 bool exist_fts_common;
5115
5116
1/2
✓ Branch 0 taken 194 times.
✗ Branch 1 not taken.
388 dict_sys_mutex_exit();
5117
1/2
✓ Branch 0 taken 194 times.
✗ Branch 1 not taken.
388 exist_fts_common = fts_check_common_tables_exist(ctx->new_table);
5118
5119
2/2
✓ Branch 0 taken 154 times.
✓ Branch 1 taken 40 times.
388 if (!exist_fts_common) {
5120 616 error = fts_create_common_tables(ctx->trx, ctx->new_table,
5121
1/2
✓ Branch 0 taken 154 times.
✗ Branch 1 not taken.
308 user_table->name.m_name, true);
5122
5123
2/4
✓ Branch 0 taken 154 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 154 times.
308 DBUG_EXECUTE_IF("innodb_test_fail_after_fts_common_table",
5124 error = DB_LOCK_WAIT_TIMEOUT;);
5125
5126
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 152 times.
308 if (error != DB_SUCCESS) {
5127
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
4 dict_sys_mutex_enter();
5128 4 goto error_handling;
5129 }
5130
5131 304 build_fts_common = true;
5132 }
5133
5134 1152 error = innobase_fts_load_stopword(ctx->new_table, nullptr,
5135
1/2
✓ Branch 0 taken 192 times.
✗ Branch 1 not taken.
384 ctx->prebuilt->trx->mysql_thd)
5136
1/2
✓ Branch 0 taken 192 times.
✗ Branch 1 not taken.
384 ? DB_SUCCESS
5137 : DB_ERROR;
5138
5139
1/2
✓ Branch 0 taken 192 times.
✗ Branch 1 not taken.
384 dict_sys_mutex_enter();
5140
5141
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 192 times.
384 if (error != DB_SUCCESS) {
5142 goto error_handling;
5143 }
5144 }
5145
5146
2/4
✓ Branch 0 taken 423 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 423 times.
846 ut_ad(trx_get_dict_operation(ctx->trx) == op);
5147 }
5148
5149
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 43970 times.
87940 assert(error == DB_SUCCESS);
5150
5151
4/4
✓ Branch 0 taken 43818 times.
✓ Branch 1 taken 152 times.
✓ Branch 2 taken 271 times.
✓ Branch 3 taken 43547 times.
87940 if (build_fts_common || fts_index) {
5152
1/2
✓ Branch 0 taken 423 times.
✗ Branch 1 not taken.
846 fts_freeze_aux_tables(ctx->new_table);
5153 }
5154
5155
1/2
✓ Branch 0 taken 43970 times.
✗ Branch 1 not taken.
87940 row_mysql_unlock_data_dictionary(ctx->prebuilt->trx);
5156
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 43970 times.
87940 ut_ad(ctx->trx == ctx->prebuilt->trx);
5157 87940 dict_locked = false;
5158
5159
2/4
✓ Branch 0 taken 43970 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 43970 times.
87940 if (dd_prepare_inplace_alter_table(ctx->prebuilt->trx->mysql_thd, user_table,
5160 ctx->new_table, old_dd_tab)) {
5161 error = DB_ERROR;
5162 }
5163
5164
1/2
✓ Branch 0 taken 43970 times.
✗ Branch 1 not taken.
87940 if (error == DB_SUCCESS) {
5165
2/2
✓ Branch 0 taken 152 times.
✓ Branch 1 taken 43818 times.
87940 if (build_fts_common) {
5166
2/4
✓ Branch 0 taken 152 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 152 times.
304 if (!fts_create_common_dd_tables(ctx->new_table)) {
5167 error = DB_ERROR;
5168 goto error_handling;
5169 }
5170 }
5171
5172
2/2
✓ Branch 0 taken 423 times.
✓ Branch 1 taken 43547 times.
87940 if (fts_index) {
5173
1/2
✓ Branch 0 taken 423 times.
✗ Branch 1 not taken.
846 error = fts_create_index_dd_tables(ctx->new_table);
5174
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 423 times.
846 if (error != DB_SUCCESS) {
5175 goto error_handling;
5176 }
5177 }
5178 }
5179
5180
4/6
✓ Branch 0 taken 43970 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 43969 times.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
87940 DBUG_EXECUTE_IF("crash_innodb_add_index_after", DBUG_SUICIDE(););
5181
5182 87938 error_handling:
5183
5184
4/4
✓ Branch 0 taken 43844 times.
✓ Branch 1 taken 152 times.
✓ Branch 2 taken 277 times.
✓ Branch 3 taken 43567 times.
87992 if (build_fts_common || fts_index) {
5185
1/2
✓ Branch 0 taken 429 times.
✗ Branch 1 not taken.
858 fts_detach_aux_tables(ctx->new_table, dict_locked);
5186 }
5187
5188 /* After an error, remove all those index definitions from the
5189 dictionary which were defined. */
5190
5191
2/5
✓ Branch 0 taken 43969 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 27 times.
87992 switch (error) {
5192 87938 case DB_SUCCESS:
5193
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 43969 times.
87938 ut_a(!dict_locked);
5194
5195
1/2
✓ Branch 0 taken 43969 times.
✗ Branch 1 not taken.
87938 ut_d(dict_sys_mutex_enter());
5196
1/2
✓ Branch 0 taken 43969 times.
✗ Branch 1 not taken.
87938 ut_d(dict_table_check_for_dup_indexes(user_table, CHECK_PARTIAL_OK));
5197
1/2
✓ Branch 0 taken 43969 times.
✗ Branch 1 not taken.
87938 ut_d(dict_sys_mutex_exit());
5198 87938 return false;
5199 case DB_TABLESPACE_EXISTS:
5200 my_error(ER_TABLESPACE_EXISTS, MYF(0), "(unknown)");
5201 break;
5202 case DB_DUPLICATE_KEY:
5203 my_error(ER_DUP_KEY, MYF(0));
5204 break;
5205 case DB_UNSUPPORTED:
5206 my_error(ER_TABLE_CANT_HANDLE_SPKEYS, MYF(0));
5207 break;
5208 54 default:
5209
1/2
✓ Branch 0 taken 27 times.
✗ Branch 1 not taken.
54 my_error_innodb(error, table_name, user_table->flags);
5210 }
5211
5212 68 error_handled:
5213
5214 68 ctx->prebuilt->trx->error_index = nullptr;
5215 68 ctx->trx->error_state = DB_SUCCESS;
5216
5217
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 32 times.
68 if (!dict_locked) {
5218
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
4 row_mysql_lock_data_dictionary(ctx->prebuilt->trx, UT_LOCATION_HERE);
5219
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
4 ut_ad(ctx->trx == ctx->prebuilt->trx);
5220 }
5221
5222
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 25 times.
68 if (new_clustered) {
5223
1/2
✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
18 if (ctx->need_rebuild()) {
5224
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 5 times.
18 if (DICT_TF2_FLAG_IS_SET(ctx->new_table, DICT_TF2_FTS)) {
5225
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
8 innobase_drop_fts_index_table(ctx->new_table, ctx->trx);
5226 }
5227
5228
1/2
✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
18 dict_table_close_and_drop(ctx->trx, ctx->new_table);
5229
5230 /* Free the log for online table rebuild, if
5231 one was allocated. */
5232
5233
1/2
✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
18 dict_index_t *clust_index = user_table->first_index();
5234
5235
1/2
✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
18 rw_lock_x_lock(&clust_index->lock, UT_LOCATION_HERE);
5236
5237
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
18 if (clust_index->online_log) {
5238 ut_ad(ctx->online);
5239 row_log_free(clust_index->online_log);
5240 clust_index->online_status = ONLINE_INDEX_COMPLETE;
5241 }
5242
5243
1/2
✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
18 rw_lock_x_unlock(&clust_index->lock);
5244 }
5245
5246 /* n_ref_count must be 1, because purge cannot
5247 be executing on this very table as we are
5248 holding MDL. */
5249
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
18 assert(user_table->get_ref_count() == 1 || ctx->online);
5250 } else {
5251
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 25 times.
50 ut_ad(!ctx->need_rebuild());
5252 50 ddl::drop_indexes(ctx->trx, user_table, true);
5253 }
5254
5255
1/2
✓ Branch 0 taken 34 times.
✗ Branch 1 not taken.
68 ut_d(dict_table_check_for_dup_indexes(user_table, CHECK_ALL_COMPLETE));
5256
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 34 times.
68 ut_ad(!user_table->drop_aborted);
5257
5258 68 err_exit:
5259 #ifdef UNIV_DEBUG
5260 /* Clear the to_be_dropped flag in the data dictionary cache. */
5261
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 39 times.
82 for (ulint i = 0; i < ctx->num_to_drop_index; i++) {
5262
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
4 assert(ctx->drop_index[i]->is_committed());
5263
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
4 assert(ctx->drop_index[i]->to_be_dropped);
5264 4 ctx->drop_index[i]->to_be_dropped = 0;
5265 }
5266 #endif /* UNIV_DEBUG */
5267
5268
1/2
✓ Branch 0 taken 39 times.
✗ Branch 1 not taken.
78 row_mysql_unlock_data_dictionary(ctx->prebuilt->trx);
5269
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 39 times.
78 ut_ad(ctx->trx == ctx->prebuilt->trx);
5270
5271 78 destroy(ctx);
5272 78 ha_alter_info->handler_ctx = nullptr;
5273
5274 78 return true;
5275 88020 }
5276
5277 /* Check whether an index is needed for the foreign key constraint.
5278 If so, if it is dropped, is there an equivalent index can play its role.
5279 @return true if the index is needed and can't be dropped */
5280 4466 [[nodiscard]] static bool innobase_check_foreign_key_index(
5281 Alter_inplace_info *ha_alter_info, /*!< in: Structure describing
5282 changes to be done by ALTER
5283 TABLE */
5284 dict_index_t *index, /*!< in: index to check */
5285 dict_table_t *indexed_table, /*!< in: table that owns the
5286 foreign keys */
5287 const char **col_names, /*!< in: column names, or NULL
5288 for indexed_table->col_names */
5289 trx_t *trx, /*!< in/out: transaction */
5290 dict_foreign_t **drop_fk, /*!< in: Foreign key constraints
5291 to drop */
5292 ulint n_drop_fk) /*!< in: Number of foreign keys
5293 to drop */
5294 {
5295
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4466 times.
4466 ut_ad(index != nullptr);
5296
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4466 times.
4466 ut_ad(indexed_table != nullptr);
5297
5298 4466 const dict_foreign_set *fks = &indexed_table->referenced_set;
5299
5300 /* Check for all FK references from other tables to the index. */
5301
2/2
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 4466 times.
4482 for (dict_foreign_set::const_iterator it = fks->begin(); it != fks->end();
5302 16 ++it) {
5303 16 dict_foreign_t *foreign = *it;
5304
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 4 times.
16 if (foreign->referenced_index != index) {
5305 12 continue;
5306 }
5307
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 ut_ad(indexed_table == foreign->referenced_table);
5308
5309 12 if (nullptr == dict_foreign_find_index(indexed_table, col_names,
5310 foreign->referenced_col_names,
5311
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 foreign->n_fields, index,
5312 /*check_charsets=*/true,
5313
2/4
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 4 times.
8 /*check_null=*/false) &&
5314
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 nullptr == innobase_find_equiv_index(foreign->referenced_col_names,
5315 4 foreign->n_fields,
5316 4 ha_alter_info->key_info_buffer,
5317
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 ha_alter_info->index_add_buffer,
5318 ha_alter_info->index_add_count)) {
5319 /* Index cannot be dropped. */
5320 trx->error_index = index;
5321 return (true);
5322 }
5323 }
5324
5325 4466 fks = &indexed_table->foreign_set;
5326
5327 /* Check for all FK references in current table using the index. */
5328
2/2
✓ Branch 0 taken 49 times.
✓ Branch 1 taken 4466 times.
4515 for (dict_foreign_set::const_iterator it = fks->begin(); it != fks->end();
5329 49 ++it) {
5330 49 dict_foreign_t *foreign = *it;
5331
2/2
✓ Branch 0 taken 27 times.
✓ Branch 1 taken 22 times.
49 if (foreign->foreign_index != index) {
5332 27 continue;
5333 }
5334
5335
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 22 times.
22 ut_ad(indexed_table == foreign->foreign_table);
5336
5337 22 if (!innobase_dropping_foreign(foreign, drop_fk, n_drop_fk) &&
5338
2/2
✓ Branch 0 taken 19 times.
✓ Branch 1 taken 1 times.
20 nullptr == dict_foreign_find_index(indexed_table, col_names,
5339 foreign->foreign_col_names,
5340
1/2
✓ Branch 0 taken 20 times.
✗ Branch 1 not taken.
20 foreign->n_fields, index,
5341 /*check_charsets=*/true,
5342
3/4
✓ Branch 0 taken 20 times.
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 22 times.
42 /*check_null=*/false) &&
5343
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 19 times.
19 nullptr == innobase_find_equiv_index(foreign->foreign_col_names,
5344 19 foreign->n_fields,
5345 19 ha_alter_info->key_info_buffer,
5346
1/2
✓ Branch 0 taken 19 times.
✗ Branch 1 not taken.
19 ha_alter_info->index_add_buffer,
5347 ha_alter_info->index_add_count)) {
5348 /* Index cannot be dropped. */
5349 trx->error_index = index;
5350 return (true);
5351 }
5352 }
5353
5354 4466 return (false);
5355 }
5356
5357 /** Rename a given index in the InnoDB data dictionary cache.
5358 @param[in,out] index index to rename
5359 @param new_name new index name */
5360 109 static void rename_index_in_cache(dict_index_t *index, const char *new_name) {
5361
1/2
✓ Branch 0 taken 109 times.
✗ Branch 1 not taken.
109 DBUG_TRACE;
5362
5363
2/4
✓ Branch 0 taken 109 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 109 times.
109 ut_ad(dict_sys_mutex_own());
5364
2/4
✓ Branch 0 taken 109 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 109 times.
109 ut_ad(rw_lock_own(dict_operation_lock, RW_LOCK_X));
5365
5366 109 size_t old_name_len = strlen(index->name);
5367 109 size_t new_name_len = strlen(new_name);
5368
5369
2/2
✓ Branch 0 taken 53 times.
✓ Branch 1 taken 56 times.
109 if (old_name_len >= new_name_len) {
5370 /* reuse the old buffer for the name if it is large enough */
5371 53 memcpy(const_cast<char *>(index->name()), new_name, new_name_len + 1);
5372 } else {
5373 /* Free the old chunk of memory if it is at the topmost
5374 place in the heap, otherwise the old chunk will be freed
5375 when the index is evicted from the cache. This code will
5376 kick-in in a repeated ALTER sequences where the old name is
5377 alternately longer/shorter than the new name:
5378 1. ALTER TABLE t RENAME INDEX a TO aa;
5379 2. ALTER TABLE t RENAME INDEX aa TO a;
5380 3. go to 1. */
5381 index->name =
5382
1/2
✓ Branch 0 taken 56 times.
✗ Branch 1 not taken.
56 mem_heap_strdup_replace(index->heap,
5383 /* Presumed topmost element of the heap: */
5384 56 index->name, old_name_len + 1, new_name);
5385 }
5386 109 }
5387
5388 /**
5389 Rename all indexes in data dictionary cache of a given table that are
5390 specified in ha_alter_info.
5391
5392 @param ctx alter context, used to fetch the list of indexes to rename
5393 @param ha_alter_info fetch the new names from here
5394 */
5395 23544 static void rename_indexes_in_cache(const ha_innobase_inplace_ctx *ctx,
5396 const Alter_inplace_info *ha_alter_info) {
5397
1/2
✓ Branch 0 taken 23544 times.
✗ Branch 1 not taken.
23544 DBUG_TRACE;
5398
5399
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23544 times.
23544 ut_ad(ctx->num_to_rename == ha_alter_info->index_rename_count);
5400
5401
2/2
✓ Branch 0 taken 109 times.
✓ Branch 1 taken 23544 times.
23653 for (ulint i = 0; i < ctx->num_to_rename; i++) {
5402 109 KEY_PAIR *pair = &ha_alter_info->index_rename_buffer[i];
5403 dict_index_t *index;
5404
5405 109 index = ctx->rename[i];
5406
5407
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 109 times.
109 ut_ad(strcmp(index->name, pair->old_key->name) == 0);
5408
5409
1/2
✓ Branch 0 taken 109 times.
✗ Branch 1 not taken.
109 rename_index_in_cache(index, pair->new_key->name);
5410 }
5411 23544 }
5412
5413 /** Fill the stored column information in s_cols list.
5414 @param[in] altered_table mysql table object
5415 @param[in] table innodb table object
5416 @param[out] s_cols list of stored column
5417 @param[out] s_heap heap for storing stored
5418 column information. */
5419 130 static void alter_fill_stored_column(const TABLE *altered_table,
5420 dict_table_t *table,
5421 dict_s_col_list **s_cols,
5422 mem_heap_t **s_heap) {
5423 130 ulint n_cols = altered_table->s->fields;
5424 130 ulint stored_col_no = 0;
5425
5426
2/2
✓ Branch 0 taken 385 times.
✓ Branch 1 taken 130 times.
515 for (ulint i = 0; i < n_cols; i++) {
5427 385 Field *field = altered_table->field[i];
5428 dict_s_col_t s_col;
5429
5430
4/4
✓ Branch 0 taken 34 times.
✓ Branch 1 taken 351 times.
✓ Branch 2 taken 12 times.
✓ Branch 3 taken 22 times.
385 if (!innobase_is_v_fld(field)) {
5431 363 stored_col_no++;
5432 }
5433
5434
2/2
✓ Branch 0 taken 373 times.
✓ Branch 1 taken 12 times.
385 if (!innobase_is_s_fld(field)) {
5435 373 continue;
5436 }
5437
5438 12 ulint num_base = field->gcol_info->non_virtual_base_columns();
5439
1/2
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
12 dict_col_t *col = table->get_col(stored_col_no);
5440
5441 12 s_col.m_col = col;
5442 12 s_col.s_pos = i;
5443
5444
1/2
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
12 if (*s_cols == nullptr) {
5445
1/2
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
12 *s_cols = ut::new_withkey<dict_s_col_list>(UT_NEW_THIS_FILE_PSI_KEY);
5446
1/2
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
12 *s_heap = mem_heap_create(100, UT_LOCATION_HERE);
5447 }
5448
5449
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 9 times.
12 if (num_base != 0) {
5450 3 s_col.base_col = static_cast<dict_col_t **>(
5451
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 mem_heap_zalloc(*s_heap, num_base * sizeof(dict_col_t)));
5452 } else {
5453 9 s_col.base_col = nullptr;
5454 }
5455
5456 12 s_col.num_base = num_base;
5457
1/2
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
12 innodb_base_col_setup_for_stored(table, field, &s_col);
5458
5459
1/2
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
12 (*s_cols)->push_back(s_col);
5460 }
5461 130 }
5462
5463 template <typename Table>
5464 32 void static adjust_row_format(TABLE *old_table, TABLE *altered_table,
5465 const Table *old_dd_tab, Table *new_dd_tab) {
5466
4/6
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 15 times.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 16 times.
32 ut_ad(old_table->s->row_type == ROW_TYPE_DEFAULT ||
5467 old_table->s->row_type == ROW_TYPE_COMPRESSED);
5468
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 16 times.
32 ut_ad(old_table->s->row_type == altered_table->s->row_type);
5469
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 16 times.
32 ut_ad(old_table->s->real_row_type != altered_table->s->real_row_type);
5470
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 16 times.
32 ut_ad(old_dd_tab->table().row_format() != new_dd_tab->table().row_format());
5471
5472 /* Revert the row_format in DD for altered table */
5473 32 new_dd_tab->table().set_row_format(old_dd_tab->table().row_format());
5474
5475 /* Revert the real_row_format in table share for altered table */
5476
3/5
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
32 switch (old_dd_tab->table().row_format()) {
5477 24 case dd::Table::RF_REDUNDANT:
5478 24 altered_table->s->real_row_type = ROW_TYPE_REDUNDANT;
5479 24 break;
5480 6 case dd::Table::RF_COMPACT:
5481 6 altered_table->s->real_row_type = ROW_TYPE_COMPACT;
5482 6 break;
5483 2 case dd::Table::RF_COMPRESSED:
5484 2 altered_table->s->real_row_type = ROW_TYPE_COMPRESSED;
5485 2 break;
5486 case dd::Table::RF_DYNAMIC:
5487 altered_table->s->real_row_type = ROW_TYPE_DYNAMIC;
5488 break;
5489 default:
5490 ut_d(ut_error);
5491 }
5492 }
5493
5494 /** Implementation of prepare_inplace_alter_table()
5495 @tparam Table dd::Table or dd::Partition
5496 @param[in] altered_table TABLE object for new version of table.
5497 @param[in,out] ha_alter_info Structure describing changes to be done
5498 by ALTER TABLE and holding data used
5499 during in-place alter.
5500 @param[in] old_dd_tab dd::Table object representing old
5501 version of the table
5502 @param[in,out] new_dd_tab dd::Table object representing new
5503 version of the table
5504 @retval true Failure
5505 @retval false Success */
5506 template <typename Table>
5507 142162 bool ha_innobase::prepare_inplace_alter_table_impl(
5508 TABLE *altered_table, Alter_inplace_info *ha_alter_info,
5509 const Table *old_dd_tab, Table *new_dd_tab) {
5510 142162 dict_index_t **drop_index = nullptr; /*!< Index to be dropped */
5511 ulint n_drop_index; /*!< Number of indexes to drop */
5512 dict_index_t **rename_index; /*!< Indexes to be dropped */
5513 ulint n_rename_index; /*!< Number of indexes to rename */
5514 dict_foreign_t **drop_fk; /*!< Foreign key constraints to drop */
5515 ulint n_drop_fk; /*!< Number of foreign keys to drop */
5516 142162 dict_foreign_t **add_fk = nullptr; /*!< Foreign key constraints to drop */
5517 ulint n_add_fk; /*!< Number of foreign keys to drop */
5518 dict_table_t *indexed_table; /*!< Table where indexes are created */
5519 mem_heap_t *heap;
5520 const char **col_names;
5521 int error;
5522 ulint max_col_len;
5523 142162 ulint add_autoinc_col_no = ULINT_UNDEFINED;
5524 142162 ulonglong autoinc_col_max_value = 0;
5525 142162 ulint fts_doc_col_no = ULINT_UNDEFINED;
5526 142162 bool add_fts_doc_id = false;
5527 142162 bool add_fts_doc_id_idx = false;
5528 142162 bool add_fts_idx = false;
5529 142162 dict_s_col_list *s_cols = nullptr;
5530 142162 mem_heap_t *s_heap = nullptr;
5531 142162 ulint encrypt_flag = 0;
5532
5533
1/2
✓ Branch 0 taken 71081 times.
✗ Branch 1 not taken.
142162 DBUG_TRACE;
5534
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 71081 times.
142162 assert(!ha_alter_info->handler_ctx);
5535
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 71081 times.
142162 assert(ha_alter_info->create_info);
5536
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 71081 times.
142162 assert(!srv_read_only_mode);
5537
5538 142162 MONITOR_ATOMIC_INC(MONITOR_PENDING_ALTER_TABLE);
5539
5540 #ifdef UNIV_DEBUG
5541
3/4
✓ Branch 0 taken 71081 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 102804 times.
✓ Branch 3 taken 71081 times.
347770 for (dict_index_t *index = m_prebuilt->table->first_index(); index;
5542
1/2
✓ Branch 0 taken 102804 times.
✗ Branch 1 not taken.
205608 index = index->next()) {
5543
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 102804 times.
205608 ut_ad(!index->to_be_dropped);
5544 }
5545 #endif /* UNIV_DEBUG */
5546
5547
1/2
✓ Branch 0 taken 71081 times.
✗ Branch 1 not taken.
142162 ut_d(dict_sys_mutex_enter());
5548
1/2
✓ Branch 0 taken 71081 times.
✗ Branch 1 not taken.
142162 ut_d(dict_table_check_for_dup_indexes(m_prebuilt->table, CHECK_ABORTED_OK));
5549
1/2
✓ Branch 0 taken 71081 times.
✗ Branch 1 not taken.
142162 ut_d(dict_sys_mutex_exit());
5550
5551 142162 indexed_table = m_prebuilt->table;
5552
5553
3/4
✓ Branch 0 taken 71081 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 71080 times.
142162 if (indexed_table->is_corrupted()) {
5554 /* The clustered index is corrupted. */
5555
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
2 my_error(ER_CHECK_NO_SUCH_TABLE, MYF(0));
5556 2 return true;
5557 }
5558
5559
2/2
✓ Branch 0 taken 29 times.
✓ Branch 1 taken 71051 times.
142160 if (dict_table_is_discarded(indexed_table)) {
5560 116 Instant_Type type = innobase_support_instant(
5561
1/2
✓ Branch 0 taken 29 times.
✗ Branch 1 not taken.
58 ha_alter_info, m_prebuilt->table, this->table, altered_table);
5562 /* Even if some operations can be done instantly without rebuilding, they
5563 are still disallowed to behave like before. */
5564 116 if (innobase_need_rebuild(
5565 58 ha_alter_info, table,
5566
6/8
✓ Branch 0 taken 29 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 19 times.
✓ Branch 3 taken 10 times.
✓ Branch 4 taken 19 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 10 times.
✓ Branch 7 taken 19 times.
96 dict_table_is_file_per_table(m_prebuilt->table)) ||
5567
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 19 times.
38 (type == Instant_Type::INSTANT_VIRTUAL_ONLY ||
5568 type == Instant_Type::INSTANT_ADD_DROP_COLUMN)) {
5569
1/2
✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
20 my_error(ER_TABLESPACE_DISCARDED, MYF(0), indexed_table->name.m_name);
5570 20 return true;
5571 }
5572 }
5573
5574
2/2
✓ Branch 0 taken 8663 times.
✓ Branch 1 taken 62407 times.
142140 if (!(ha_alter_info->handler_flags & ~INNOBASE_INPLACE_IGNORE)) {
5575 /* Nothing to do. Since there is no MDL protected, don't
5576 try to drop aborted indexes here. */
5577
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8663 times.
17326 assert(m_prebuilt->trx->dict_operation_lock_mode == 0);
5578 17326 return false;
5579 }
5580
5581
2/2
✓ Branch 0 taken 2303 times.
✓ Branch 1 taken 60104 times.
124814 if (is_instant(ha_alter_info)) {
5582 9212 Instant_Type type = innobase_support_instant(ha_alter_info, indexed_table,
5583
1/2
✓ Branch 0 taken 2303 times.
✗ Branch 1 not taken.
4606 this->table, altered_table);
5584
5585
2/2
✓ Branch 0 taken 2030 times.
✓ Branch 1 taken 273 times.
4606 if (type == Instant_Type::INSTANT_ADD_DROP_COLUMN) {
5586
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2030 times.
4060 ut_a(indexed_table->current_row_version < MAX_ROW_VERSION);
5587 }
5588
5589 4606 return false;
5590 }
5591
5592 /* ALTER TABLE will not implicitly move a table from a single-table
5593 tablespace to the system tablespace when innodb_file_per_table=OFF.
5594 But it will implicitly move a table from the system tablespace to a
5595 single-table tablespace if innodb_file_per_table = ON.
5596 Tables found in a general tablespace will stay there unless ALTER
5597 TABLE contains another TABLESPACE=name. If that is found it will
5598 explicitly move a table to the named tablespace.
5599 So if you specify TABLESPACE=`innodb_system` a table can be moved
5600 into the system tablespace from either a general or file-per-table
5601 tablespace. But from then on, it is labeled as using a shared space
5602 (the create options have tablespace=='innodb_system' and the
5603 SHARED_SPACE flag is set in the table flags) so it can no longer be
5604 implicitly moved to a file-per-table tablespace. */
5605
1/2
✓ Branch 0 taken 60104 times.
✗ Branch 1 not taken.
120208 bool in_system_space = fsp_is_system_or_temp_tablespace(indexed_table->space);
5606 120208 bool is_file_per_table =
5607
4/4
✓ Branch 0 taken 59947 times.
✓ Branch 1 taken 157 times.
✓ Branch 2 taken 41198 times.
✓ Branch 3 taken 18749 times.
120208 !in_system_space && !DICT_TF_HAS_SHARED_SPACE(indexed_table->flags);
5608 #ifdef UNIV_DEBUG
5609 120208 bool in_general_space =
5610
4/4
✓ Branch 0 taken 59947 times.
✓ Branch 1 taken 157 times.
✓ Branch 2 taken 18749 times.
✓ Branch 3 taken 41198 times.
120208 !in_system_space && DICT_TF_HAS_SHARED_SPACE(indexed_table->flags);
5611
5612 /* The table being altered can only be in a system tablespace,
5613 or its own file-per-table tablespace, or a general tablespace. */
5614
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 60104 times.
120208 ut_ad(1 == in_system_space + is_file_per_table + in_general_space);
5615 #endif /* UNIV_DEBUG */
5616
5617 /* If server has passed a changed row format in the new table definition and
5618 the table isn't going to be rebuilt, revert that row_format change because it
5619 is an implicit change to the previously selected default row format. We want
5620 to keep the table using the original default row_format. */
5621
11/15
✓ Branch 0 taken 7102 times.
✓ Branch 1 taken 53002 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 7102 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 53002 times.
✓ Branch 6 taken 7102 times.
✓ Branch 7 taken 150 times.
✓ Branch 8 taken 52852 times.
✓ Branch 9 taken 7118 times.
✓ Branch 10 taken 52986 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 7102 times.
✗ Branch 13 not taken.
✓ Branch 14 taken 7102 times.
120508 if (old_dd_tab->table().row_format() != new_dd_tab->table().row_format() &&
5622
2/2
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 134 times.
300 !innobase_need_rebuild(ha_alter_info, altered_table,
5623
1/2
✓ Branch 0 taken 150 times.
✗ Branch 1 not taken.
300 dict_table_is_file_per_table(m_prebuilt->table))) {
5624
1/2
✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
32 adjust_row_format(this->table, altered_table, old_dd_tab, new_dd_tab);
5625 }
5626
5627 /* Make a copy for existing tablespace name */
5628 120208 char tablespace[NAME_LEN] = {'\0'};
5629
2/2
✓ Branch 0 taken 18832 times.
✓ Branch 1 taken 41272 times.
120208 if (indexed_table->tablespace) {
5630 37664 strcpy(tablespace, indexed_table->tablespace());
5631 }
5632
5633 226212 adjust_encryption_key_id(ha_alter_info->create_info,
5634
1/2
✓ Branch 0 taken 60104 times.
✗ Branch 1 not taken.
120208 &(new_dd_tab->options()));
5635
5636
3/4
✓ Branch 0 taken 18832 times.
✓ Branch 1 taken 41272 times.
✓ Branch 2 taken 60104 times.
✗ Branch 3 not taken.
120208 create_table_info_t info(m_user_thd, altered_table,
5637 ha_alter_info->create_info, nullptr, nullptr,
5638 120208 indexed_table->tablespace ? tablespace : nullptr,
5639 is_file_per_table, false, 0, 0, false);
5640
5641
1/2
✓ Branch 0 taken 60104 times.
✗ Branch 1 not taken.
120208 info.set_tablespace_type(is_file_per_table);
5642
5643
5/6
✓ Branch 0 taken 31346 times.
✓ Branch 1 taken 28758 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 31346 times.
✓ Branch 4 taken 28758 times.
✓ Branch 5 taken 31346 times.
182900 if (ha_alter_info->handler_flags & Alter_inplace_info::CHANGE_CREATE_OPTION ||
5644 62692 (Encryption::should_be_keyring_encrypted(
5645 62692 ha_alter_info->create_info->explicit_encryption,
5646
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
62692 ha_alter_info->create_info->encrypt_type.str) &&
5647 innobase_spatial_exist(
5648 altered_table))) { // We need to make sure spatial index was not
5649 // added if this is to be keyring encrypted
5650
1/2
✓ Branch 0 taken 28758 times.
✗ Branch 1 not taken.
57516 const char *invalid_opt = info.create_options_are_invalid();
5651
2/2
✓ Branch 0 taken 101 times.
✓ Branch 1 taken 28657 times.
57516 if (invalid_opt != nullptr) {
5652
2/4
✓ Branch 0 taken 101 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 101 times.
✗ Branch 3 not taken.
202 my_error(ER_ILLEGAL_HA_CREATE_OPTION, MYF(0), table_type(), invalid_opt);
5653 202 goto err_exit_no_heap;
5654 }
5655 }
5656
5657 /* Check if any index name is reserved. */
5658
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 60002 times.
120006 if (innobase_index_name_is_reserved(m_user_thd,
5659 120006 ha_alter_info->key_info_buffer,
5660
1/2
✓ Branch 0 taken 60003 times.
✗ Branch 1 not taken.
120006 ha_alter_info->key_count)) {
5661 2 err_exit_no_heap:
5662
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 117 times.
234 assert(m_prebuilt->trx->dict_operation_lock_mode == 0);
5663
1/2
✓ Branch 0 taken 117 times.
✗ Branch 1 not taken.
234 if (ha_alter_info->handler_flags & ~INNOBASE_INPLACE_IGNORE) {
5664
1/2
✓ Branch 0 taken 117 times.
✗ Branch 1 not taken.
234 online_retry_drop_dict_indexes(m_prebuilt->table, false);
5665 }
5666 234 return true;
5667 }
5668
5669 120004 indexed_table = m_prebuilt->table;
5670
5671 /* Check that index keys are sensible */
5672
1/2
✓ Branch 0 taken 60002 times.
✗ Branch 1 not taken.
120004 error = innobase_check_index_keys(ha_alter_info, indexed_table);
5673
5674
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 60002 times.
120004 if (error) {
5675 goto err_exit_no_heap;
5676 }
5677
5678 /* Prohibit renaming a column to something that the table
5679 already contains. */
5680
2/2
✓ Branch 0 taken 103 times.
✓ Branch 1 taken 59899 times.
120004 if (ha_alter_info->handler_flags & Alter_inplace_info::ALTER_COLUMN_NAME) {
5681
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 101 times.
206 if (!ok_to_rename_column(ha_alter_info, table, altered_table,
5682
1/2
✓ Branch 0 taken 103 times.
✗ Branch 1 not taken.
206 m_prebuilt->table, false, true)) {
5683 4 goto err_exit_no_heap;
5684 }
5685 }
5686
5687
3/4
✓ Branch 0 taken 60000 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 59998 times.
120000 if (!info.innobase_table_flags()) {
5688 4 goto err_exit_no_heap;
5689 }
5690
5691 /* create_table_info_t::innobase_table_flags does not set encryption
5692 flags. There are places where it is done afterwards, there are places
5693 where it isn't done. We need to inspect all code paths and check if
5694 encryption flag can be set in one place. */
5695
2/2
✓ Branch 0 taken 959 times.
✓ Branch 1 taken 59039 times.
119996 if (Encryption::is_master_key_encryption(
5696 119996 ha_alter_info->create_info->encrypt_type.str)) {
5697 /* Set the encryption flag. */
5698 1918 byte *master_key = nullptr;
5699 uint32_t master_key_id;
5700
5701 /* Check if keyring is ready. */
5702 1918 Encryption::get_master_key(&master_key_id, &master_key);
5703
5704
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 959 times.
1918 if (master_key == nullptr) {
5705 goto err_exit_no_heap;
5706 } else {
5707
1/2
✓ Branch 0 taken 959 times.
✗ Branch 1 not taken.
1918 my_free(master_key);
5708 1918 encrypt_flag = DICT_TF2_ENCRYPTION_FILE_PER_TABLE;
5709 }
5710 }
5711
5712 119996 max_col_len = DICT_MAX_FIELD_LEN_BY_FORMAT_FLAG(info.flags());
5713
5714 /* Check each index's column length to make sure they do not
5715 exceed limit */
5716
2/2
✓ Branch 0 taken 10653 times.
✓ Branch 1 taken 59994 times.
141294 for (ulint i = 0; i < ha_alter_info->index_add_count; i++) {
5717 21306 const KEY *key =
5718 21306 &ha_alter_info->key_info_buffer[ha_alter_info->index_add_buffer[i]];
5719
5720
2/2
✓ Branch 0 taken 440 times.
✓ Branch 1 taken 10213 times.
21306 if (key->flags & HA_FULLTEXT) {
5721 /* The column length does not matter for
5722 fulltext search indexes. But, UNIQUE
5723 fulltext indexes are not supported. */
5724
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 440 times.
880 assert(!(key->flags & HA_NOSAME));
5725
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 440 times.
880 assert(!(key->flags & HA_KEYFLAG_MASK &
5726 ~(HA_FULLTEXT | HA_PACK_KEY | HA_BINARY_PACK_KEY)));
5727 880 add_fts_idx = true;
5728 880 continue;
5729 }
5730
5731
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 10209 times.
20426 if (innobase_check_column_length(max_col_len, key)) {
5732
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
8 my_error(ER_INDEX_COLUMN_TOO_LONG, MYF(0), max_col_len);
5733 8 goto err_exit_no_heap;
5734 }
5735 }
5736
5737 /* Check existing index definitions for too-long column
5738 prefixes as well, in case max_col_len shrunk. */
5739
3/4
✓ Branch 0 taken 59994 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 88925 times.
✓ Branch 3 taken 59994 times.
297838 for (const dict_index_t *index = indexed_table->first_index(); index;
5740
1/2
✓ Branch 0 taken 88925 times.
✗ Branch 1 not taken.
177850 index = index->next()) {
5741
2/2
✓ Branch 0 taken 1079 times.
✓ Branch 1 taken 87846 times.
177850 if (index->type & DICT_FTS) {
5742
1/6
✗ Branch 0 not taken.
✓ Branch 1 taken 1079 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
2158 assert(index->type == DICT_FTS || index->is_corrupted());
5743
5744 /* We need to drop any corrupted fts indexes
5745 before we add a new fts index. */
5746
3/4
✓ Branch 0 taken 907 times.
✓ Branch 1 taken 172 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 907 times.
2158 if (add_fts_idx && index->type & DICT_CORRUPT) {
5747 ib_errf(m_user_thd, IB_LOG_LEVEL_ERROR, ER_INNODB_INDEX_CORRUPT,
5748 "Fulltext index '%s' is corrupt. "
5749 "you should drop this index first.",
5750 index->name());
5751
5752 goto err_exit_no_heap;
5753 }
5754
5755 2158 continue;
5756 }
5757
5758
3/4
✓ Branch 0 taken 669558 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 581712 times.
✓ Branch 3 taken 87846 times.
1339116 for (ulint i = 0; i < dict_index_get_n_fields(index); i++) {
5759
1/2
✓ Branch 0 taken 581712 times.
✗ Branch 1 not taken.
1163424 const dict_field_t *field = index->get_field(i);
5760
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 581712 times.
1163424 if (field->prefix_len > max_col_len) {
5761 my_error(ER_INDEX_COLUMN_TOO_LONG, MYF(0), max_col_len);
5762 goto err_exit_no_heap;
5763 }
5764 }
5765 }
5766
5767 119988 n_drop_index = 0;
5768 119988 n_drop_fk = 0;
5769
5770
1/2
✓ Branch 0 taken 59994 times.
✗ Branch 1 not taken.
119988 if (ha_alter_info->handler_flags &
5771 (INNOBASE_ALTER_NOREBUILD | INNOBASE_ALTER_REBUILD)) {
5772
1/2
✓ Branch 0 taken 59994 times.
✗ Branch 1 not taken.
119988 heap = mem_heap_create(1024, UT_LOCATION_HERE);
5773
5774
2/2
✓ Branch 0 taken 101 times.
✓ Branch 1 taken 59893 times.
119988 if (ha_alter_info->handler_flags & Alter_inplace_info::ALTER_COLUMN_NAME) {
5775
1/2
✓ Branch 0 taken 101 times.
✗ Branch 1 not taken.
202 col_names = innobase_get_col_names(ha_alter_info, altered_table, table,
5776 indexed_table, heap);
5777 } else {
5778 119786 col_names = nullptr;
5779 }
5780 } else {
5781 heap = nullptr;
5782 col_names = nullptr;
5783 }
5784
5785
2/2
✓ Branch 0 taken 57 times.
✓ Branch 1 taken 59937 times.
119988 if (ha_alter_info->handler_flags & Alter_inplace_info::DROP_FOREIGN_KEY) {
5786
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 57 times.
114 assert(ha_alter_info->alter_info->drop_list.size() > 0);
5787
5788 drop_fk = static_cast<dict_foreign_t **>(
5789
1/2
✓ Branch 0 taken 57 times.
✗ Branch 1 not taken.
114 mem_heap_alloc(heap, ha_alter_info->alter_info->drop_list.size() *
5790 sizeof(dict_foreign_t *)));
5791
5792
3/4
✓ Branch 0 taken 57 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 66 times.
✓ Branch 3 taken 57 times.
246 for (const Alter_drop *drop : ha_alter_info->alter_info->drop_list) {
5793
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 58 times.
132 if (drop->type != Alter_drop::FOREIGN_KEY) {
5794 16 continue;
5795 }
5796
5797 116 for (dict_foreign_set::iterator it =
5798 116 m_prebuilt->table->foreign_set.begin();
5799
1/2
✓ Branch 0 taken 62 times.
✗ Branch 1 not taken.
124 it != m_prebuilt->table->foreign_set.end(); ++it) {
5800 124 dict_foreign_t *foreign = *it;
5801 124 const char *fid = strchr(foreign->id, '/');
5802
5803
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 62 times.
124 assert(fid);
5804 /* If no database/ prefix was present in
5805 the FOREIGN KEY constraint name, compare
5806 to the full constraint name. */
5807
1/2
✓ Branch 0 taken 62 times.
✗ Branch 1 not taken.
124 fid = fid ? fid + 1 : foreign->id;
5808
5809
3/4
✓ Branch 0 taken 62 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 58 times.
✓ Branch 3 taken 4 times.
124 if (!my_strcasecmp(system_charset_info, fid, drop->name)) {
5810 116 drop_fk[n_drop_fk++] = foreign;
5811 116 goto found_fk;
5812 }
5813 }
5814
5815 /*
5816 Since we check that foreign key to be dropped exists on SQL-layer,
5817 we should not come here unless there is some bug and data-dictionary
5818 and InnoDB dictionary cache got out of sync.
5819 */
5820 assert(0);
5821 my_error(ER_CANT_DROP_FIELD_OR_KEY, MYF(0), drop->name);
5822 goto err_exit;
5823 116 found_fk:
5824 116 continue;
5825 }
5826
5827
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 57 times.
114 assert(n_drop_fk > 0);
5828 } else {
5829 119874 drop_fk = nullptr;
5830 }
5831
5832
2/2
✓ Branch 0 taken 4257 times.
✓ Branch 1 taken 55737 times.
119988 if (ha_alter_info->index_drop_count) {
5833 8514 dict_index_t *drop_primary = nullptr;
5834
5835
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4257 times.
8514 assert(ha_alter_info->handler_flags &
5836 (Alter_inplace_info::DROP_INDEX |
5837 Alter_inplace_info::DROP_UNIQUE_INDEX |
5838 Alter_inplace_info::DROP_PK_INDEX));
5839 /* Check which indexes to drop. */
5840 17028 drop_index = static_cast<dict_index_t **>(mem_heap_alloc(
5841
1/2
✓ Branch 0 taken 4257 times.
✗ Branch 1 not taken.
8514 heap, (ha_alter_info->index_drop_count + 1) * sizeof *drop_index));
5842
5843
2/2
✓ Branch 0 taken 4461 times.
✓ Branch 1 taken 4257 times.
17436 for (uint i = 0; i < ha_alter_info->index_drop_count; i++) {
5844 8922 const KEY *key = ha_alter_info->index_drop_buffer[i];
5845 dict_index_t *index =
5846
1/2
✓ Branch 0 taken 4461 times.
✗ Branch 1 not taken.
8922 dict_table_get_index_on_name(indexed_table, key->name);
5847
5848
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4461 times.
8922 if (!index) {
5849 push_warning_printf(m_user_thd, Sql_condition::SL_WARNING,
5850 HA_ERR_WRONG_INDEX,
5851 "InnoDB could not find key"
5852 " with name %s",
5853 key->name);
5854 } else {
5855
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4461 times.
8922 ut_ad(!index->to_be_dropped);
5856
3/4
✓ Branch 0 taken 4461 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3797 times.
✓ Branch 3 taken 664 times.
8922 if (!index->is_clustered()) {
5857 7594 drop_index[n_drop_index++] = index;
5858 } else {
5859 1328 drop_primary = index;
5860 }
5861 }
5862 }
5863
5864 /* If all FULLTEXT indexes were removed, drop an
5865 internal FTS_DOC_ID_INDEX as well, unless it exists in
5866 the table. */
5867
5868 8514 if (innobase_fulltext_exist(table) &&
5869
6/6
✓ Branch 0 taken 117 times.
✓ Branch 1 taken 4140 times.
✓ Branch 2 taken 96 times.
✓ Branch 3 taken 21 times.
✓ Branch 4 taken 4240 times.
✓ Branch 5 taken 17 times.
8706 !innobase_fulltext_exist(altered_table) &&
5870
2/2
✓ Branch 0 taken 17 times.
✓ Branch 1 taken 79 times.
192 !DICT_TF2_FLAG_IS_SET(indexed_table, DICT_TF2_FTS_HAS_DOC_ID)) {
5871 34 dict_index_t *fts_doc_index = indexed_table->fts_doc_id_index;
5872
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 17 times.
34 ut_ad(fts_doc_index);
5873
5874 // Add some fault tolerance for non-debug builds.
5875
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 17 times.
34 if (fts_doc_index == nullptr) {
5876 goto check_if_can_drop_indexes;
5877 }
5878
5879
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 17 times.
34 assert(!fts_doc_index->to_be_dropped);
5880
5881
2/2
✓ Branch 0 taken 32 times.
✓ Branch 1 taken 5 times.
74 for (uint i = 0; i < table->s->keys; i++) {
5882
3/4
✓ Branch 0 taken 32 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 12 times.
✓ Branch 3 taken 20 times.
64 if (!my_strcasecmp(system_charset_info, FTS_DOC_ID_INDEX_NAME,
5883 table->key_info[i].name)) {
5884 /* The index exists in the MySQL
5885 data dictionary. Do not drop it,
5886 even though it is no longer needed
5887 by InnoDB fulltext search. */
5888 24 goto check_if_can_drop_indexes;
5889 }
5890 }
5891
5892 10 drop_index[n_drop_index++] = fts_doc_index;
5893 }
5894
5895 8480 check_if_can_drop_indexes:
5896 /* Check if the indexes can be dropped. */
5897
5898 /* Prevent a race condition between DROP INDEX and
5899 CREATE TABLE adding FOREIGN KEY constraints. */
5900
1/2
✓ Branch 0 taken 4257 times.
✗ Branch 1 not taken.
8514 row_mysql_lock_data_dictionary(m_prebuilt->trx, UT_LOCATION_HERE);
5901
5902
2/2
✓ Branch 0 taken 658 times.
✓ Branch 1 taken 3599 times.
8514 if (!n_drop_index) {
5903 1316 drop_index = nullptr;
5904 } else {
5905 /* Flag all indexes that are to be dropped. */
5906
2/2
✓ Branch 0 taken 3802 times.
✓ Branch 1 taken 3599 times.
14802 for (ulint i = 0; i < n_drop_index; i++) {
5907
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3802 times.
7604 ut_ad(!drop_index[i]->to_be_dropped);
5908 7604 drop_index[i]->to_be_dropped = 1;
5909 }
5910 }
5911
5912
2/2
✓ Branch 0 taken 3802 times.
✓ Branch 1 taken 4257 times.
16118 for (uint i = 0; i < n_drop_index; i++) {
5913 7604 dict_index_t *index = drop_index[i];
5914
5915
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3802 times.
7604 if (innobase_check_foreign_key_index(ha_alter_info, index, indexed_table,
5916
1/2
✓ Branch 0 taken 3802 times.
✗ Branch 1 not taken.
7604 col_names, m_prebuilt->trx, drop_fk,
5917 n_drop_fk)) {
5918 row_mysql_unlock_data_dictionary(m_prebuilt->trx);
5919 m_prebuilt->trx->error_index = index;
5920 print_error(HA_ERR_DROP_INDEX_FK, MYF(0));
5921 goto err_exit;
5922 }
5923 }
5924
5925 /* If a primary index is dropped, need to check
5926 any depending foreign constraints get affected */
5927
4/6
✓ Branch 0 taken 664 times.
✓ Branch 1 taken 3593 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 664 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 4257 times.
9842 if (drop_primary && innobase_check_foreign_key_index(
5928 ha_alter_info, drop_primary, indexed_table,
5929
1/2
✓ Branch 0 taken 664 times.
✗ Branch 1 not taken.
1328 col_names, m_prebuilt->trx, drop_fk, n_drop_fk)) {
5930 row_mysql_unlock_data_dictionary(m_prebuilt->trx);
5931 print_error(HA_ERR_DROP_INDEX_FK, MYF(0));
5932 goto err_exit;
5933 }
5934
5935
1/2
✓ Branch 0 taken 4257 times.
✗ Branch 1 not taken.
8514 row_mysql_unlock_data_dictionary(m_prebuilt->trx);
5936 } else {
5937 111474 drop_index = nullptr;
5938 }
5939
5940 119988 n_rename_index = ha_alter_info->index_rename_count;
5941 119988 rename_index = nullptr;
5942
5943 /* Create a list of dict_index_t objects that are to be renamed,
5944 also checking for requests to rename nonexistent indexes. If
5945 the table is going to be rebuilt (new_clustered == true in
5946 prepare_inplace_alter_table_dict()), then this can be skipped,
5947 but we don't for simplicity (we have not determined the value of
5948 new_clustered yet). */
5949
2/2
✓ Branch 0 taken 118 times.
✓ Branch 1 taken 59876 times.
119988 if (n_rename_index > 0) {
5950 rename_index = static_cast<dict_index_t **>(
5951
1/2
✓ Branch 0 taken 118 times.
✗ Branch 1 not taken.
236 mem_heap_alloc(heap, n_rename_index * sizeof(*rename_index)));
5952
2/2
✓ Branch 0 taken 119 times.
✓ Branch 1 taken 118 times.
474 for (ulint i = 0; i < n_rename_index; i++) {
5953 dict_index_t *index;
5954 238 const char *old_name =
5955 238 ha_alter_info->index_rename_buffer[i].old_key->name;
5956
5957
1/2
✓ Branch 0 taken 119 times.
✗ Branch 1 not taken.
238 index = dict_table_get_index_on_name(indexed_table, old_name);
5958
5959
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 119 times.
238 if (index == nullptr) {
5960 my_error(ER_KEY_DOES_NOT_EXITS, MYF(0), old_name,
5961 m_prebuilt->table->name.m_name);
5962 goto err_exit;
5963 }
5964
5965 238 rename_index[i] = index;
5966 }
5967 }
5968
5969 119988 n_add_fk = 0;
5970
5971
2/2
✓ Branch 0 taken 130 times.
✓ Branch 1 taken 59864 times.
119988 if (ha_alter_info->handler_flags & Alter_inplace_info::ADD_FOREIGN_KEY) {
5972
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 130 times.
260 ut_ad(!m_prebuilt->trx->check_foreigns);
5973
5974
1/2
✓ Branch 0 taken 130 times.
✗ Branch 1 not taken.
260 alter_fill_stored_column(altered_table, m_prebuilt->table, &s_cols,
5975 &s_heap);
5976
5977
1/2
✓ Branch 0 taken 130 times.
✗ Branch 1 not taken.
260 add_fk = static_cast<dict_foreign_t **>(mem_heap_zalloc(
5978 heap,
5979 260 ha_alter_info->alter_info->key_list.size() * sizeof(dict_foreign_t *)));
5980
5981
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 124 times.
260 if (!innobase_get_foreign_key_info(ha_alter_info, table_share,
5982 260 m_prebuilt->table, col_names, drop_index,
5983 n_drop_index, add_fk, &n_add_fk,
5984
1/2
✓ Branch 0 taken 130 times.
✗ Branch 1 not taken.
260 m_prebuilt->trx, s_cols)) {
5985 12 err_exit:
5986
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 7 times.
14 if (n_drop_index) {
5987 row_mysql_lock_data_dictionary(m_prebuilt->trx, UT_LOCATION_HERE);
5988
5989 /* Clear the to_be_dropped flags, which might
5990 have been set at this point. */
5991 for (ulint i = 0; i < n_drop_index; i++) {
5992 ut_ad(drop_index[i]->is_committed());
5993 drop_index[i]->to_be_dropped = 0;
5994 }
5995
5996 row_mysql_unlock_data_dictionary(m_prebuilt->trx);
5997 }
5998
5999
1/2
✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
14 if (heap) {
6000
1/2
✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
14 mem_heap_free(heap);
6001 }
6002
6003
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 4 times.
14 if (s_cols != nullptr) {
6004 6 ut::delete_(s_cols);
6005
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
6 mem_heap_free(s_heap);
6006 }
6007
6008 14 goto err_exit_no_heap;
6009 }
6010
6011
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 115 times.
248 if (s_cols != nullptr) {
6012 18 ut::delete_(s_cols);
6013
1/2
✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
18 mem_heap_free(s_heap);
6014 }
6015 }
6016
6017
4/4
✓ Branch 0 taken 56035 times.
✓ Branch 1 taken 3953 times.
✓ Branch 2 taken 15883 times.
✓ Branch 3 taken 44105 times.
232046 if (!(ha_alter_info->handler_flags & INNOBASE_ALTER_DATA) ||
6018
2/2
✓ Branch 0 taken 24639 times.
✓ Branch 1 taken 31396 times.
112070 ((ha_alter_info->handler_flags & ~INNOBASE_INPLACE_IGNORE) ==
6019 49278 Alter_inplace_info::CHANGE_CREATE_OPTION &&
6020
2/2
✓ Branch 0 taken 11930 times.
✓ Branch 1 taken 12709 times.
49278 !innobase_need_rebuild(
6021 49278 ha_alter_info, table,
6022
1/2
✓ Branch 0 taken 24639 times.
✗ Branch 1 not taken.
49278 dict_table_is_file_per_table(m_prebuilt->table)))) {
6023
1/2
✓ Branch 0 taken 15883 times.
✗ Branch 1 not taken.
31766 if (heap) {
6024
1/2
✓ Branch 0 taken 15883 times.
✗ Branch 1 not taken.
63532 ha_alter_info->handler_ctx = new (m_user_thd->mem_root)
6025 ha_innobase_inplace_ctx(m_prebuilt, drop_index, n_drop_index,
6026 rename_index, n_rename_index, drop_fk,
6027 n_drop_fk, add_fk, n_add_fk,
6028
1/2
✓ Branch 0 taken 15883 times.
✗ Branch 1 not taken.
31766 ha_alter_info->online, heap, indexed_table,
6029 col_names, ULINT_UNDEFINED, 0, 0);
6030 }
6031
6032
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 15883 times.
31766 assert(m_prebuilt->trx->dict_operation_lock_mode == 0);
6033
1/2
✓ Branch 0 taken 15883 times.
✗ Branch 1 not taken.
31766 if (ha_alter_info->handler_flags & ~INNOBASE_INPLACE_IGNORE) {
6034
1/2
✓ Branch 0 taken 15883 times.
✗ Branch 1 not taken.
31766 online_retry_drop_dict_indexes(m_prebuilt->table, false);
6035 }
6036
6037 63532 if ((ha_alter_info->handler_flags &
6038
3/4
✓ Branch 0 taken 87 times.
✓ Branch 1 taken 15796 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 15883 times.
31940 Alter_inplace_info::DROP_VIRTUAL_COLUMN) &&
6039
2/4
✓ Branch 0 taken 87 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 87 times.
174 prepare_inplace_drop_virtual(ha_alter_info, table)) {
6040 return true;
6041 }
6042
6043 63532 if ((ha_alter_info->handler_flags &
6044
3/4
✓ Branch 0 taken 121 times.
✓ Branch 1 taken 15762 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 15883 times.
32008 Alter_inplace_info::ADD_VIRTUAL_COLUMN) &&
6045
2/4
✓ Branch 0 taken 121 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 121 times.
242 prepare_inplace_add_virtual(ha_alter_info, altered_table, table)) {
6046 return true;
6047 }
6048
6049
1/2
✓ Branch 0 taken 15883 times.
✗ Branch 1 not taken.
31766 if (ha_alter_info->handler_ctx != nullptr) {
6050 31766 ha_innobase_inplace_ctx *ctx =
6051 static_cast<ha_innobase_inplace_ctx *>(ha_alter_info->handler_ctx);
6052 63532 if ((ha_alter_info->handler_flags &
6053 23860 Alter_inplace_info::CHANGE_CREATE_OPTION) &&
6054 23860 ha_alter_info->create_info
6055
5/6
✓ Branch 0 taken 11930 times.
✓ Branch 1 taken 3953 times.
✓ Branch 2 taken 5033 times.
✓ Branch 3 taken 6897 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 15883 times.
41832 ->m_implicit_tablespace_autoextend_size_change &&
6056
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5033 times.
10066 prepare_inplace_change_implicit_tablespace_option(
6057
1/2
✓ Branch 0 taken 5033 times.
✗ Branch 1 not taken.
10066 m_user_thd, ha_alter_info, ctx->old_table)) {
6058 return true;
6059 }
6060
1/2
✓ Branch 0 taken 15883 times.
✗ Branch 1 not taken.
31766 return dd_prepare_inplace_alter_table(m_user_thd, ctx->old_table,
6061 31766 ctx->new_table, old_dd_tab);
6062 } else {
6063 return false;
6064 }
6065 }
6066
6067 /* If we are to build a full-text search index, check whether
6068 the table already has a DOC ID column. If not, we will need to
6069 add a Doc ID hidden column and rebuild the primary index */
6070
2/2
✓ Branch 0 taken 462 times.
✓ Branch 1 taken 43643 times.
88210 if (innobase_fulltext_exist(altered_table)) {
6071 ulint doc_col_no;
6072 924 ulint num_v = 0;
6073
6074
3/4
✓ Branch 0 taken 462 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 126 times.
✓ Branch 3 taken 336 times.
924 if (!innobase_fts_check_doc_id_col(m_prebuilt->table, altered_table,
6075 &fts_doc_col_no, &num_v)) {
6076 252 fts_doc_col_no = altered_table->s->fields - num_v;
6077 252 add_fts_doc_id = true;
6078 252 add_fts_doc_id_idx = true;
6079
6080
1/2
✓ Branch 0 taken 126 times.
✗ Branch 1 not taken.
252 push_warning_printf(m_user_thd, Sql_condition::SL_WARNING,
6081 HA_ERR_WRONG_INDEX,
6082 "InnoDB rebuilding table to add"
6083 " column " FTS_DOC_ID_COL_NAME);
6084
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 335 times.
672 } else if (fts_doc_col_no == ULINT_UNDEFINED) {
6085 2 goto err_exit;
6086 }
6087
6088
3/6
✓ Branch 0 taken 461 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 141 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 320 times.
✗ Branch 5 not taken.
922 switch (innobase_fts_check_doc_id_index(m_prebuilt->table, altered_table,
6089 &doc_col_no)) {
6090 282 case FTS_NOT_EXIST_DOC_ID_INDEX:
6091 282 add_fts_doc_id_idx = true;
6092 282 break;
6093 case FTS_INCORRECT_DOC_ID_INDEX:
6094 my_error(ER_INNODB_FT_WRONG_DOCID_INDEX, MYF(0), FTS_DOC_ID_INDEX_NAME);
6095 goto err_exit;
6096 640 case FTS_EXIST_DOC_ID_INDEX:
6097
3/6
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 304 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 16 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
640 assert(doc_col_no == fts_doc_col_no || doc_col_no == ULINT_UNDEFINED ||
6098 (ha_alter_info->handler_flags &
6099 (Alter_inplace_info::ALTER_STORED_COLUMN_ORDER |
6100 Alter_inplace_info::DROP_STORED_COLUMN |
6101 Alter_inplace_info::ADD_STORED_BASE_COLUMN)));
6102 }
6103 }
6104
6105 /* See if an AUTO_INCREMENT column was added. */
6106 88208 uint i = 0;
6107 88208 ulint num_v = 0;
6108 88208 List_iterator_fast<Create_field> cf_it(
6109
1/2
✓ Branch 0 taken 44104 times.
✗ Branch 1 not taken.
88208 ha_alter_info->alter_info->create_list);
6110
2/2
✓ Branch 0 taken 303855 times.
✓ Branch 1 taken 44104 times.
695918 while (const Create_field *new_field = cf_it++) {
6111 const Field *field;
6112
6113
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 303855 times.
607710 assert(i < altered_table->s->fields);
6114
6115
2/2
✓ Branch 0 taken 3658706 times.
✓ Branch 1 taken 17788 times.
7352988 for (uint old_i = 0; table->field[old_i]; old_i++) {
6116
2/2
✓ Branch 0 taken 286067 times.
✓ Branch 1 taken 3372639 times.
7317412 if (new_field->field == table->field[old_i]) {
6117 572134 goto found_col;
6118 }
6119 }
6120
6121 /* This is an added column. */
6122
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 17788 times.
35576 assert(!new_field->field);
6123
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 17788 times.
35576 assert(ha_alter_info->handler_flags & Alter_inplace_info::ADD_COLUMN);
6124
6125 35576 field = altered_table->field[i];
6126
6127
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 17788 times.
35576 assert(((field->auto_flags & Field::NEXT_NUMBER) != 0) ==
6128 field->is_flag_set(AUTO_INCREMENT_FLAG));
6129
6130
2/2
✓ Branch 0 taken 17737 times.
✓ Branch 1 taken 51 times.
35576 if (field->is_flag_set(AUTO_INCREMENT_FLAG)) {
6131
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 51 times.
102 if (add_autoinc_col_no != ULINT_UNDEFINED) {
6132 /* This should have been blocked earlier. */
6133 my_error(ER_WRONG_AUTO_KEY, MYF(0));
6134 ut_d(ut_error);
6135 ut_o(goto err_exit);
6136 }
6137
6138 /* Get the col no of the old table non-virtual column array */
6139 102 add_autoinc_col_no = i - num_v;
6140
6141
1/2
✓ Branch 0 taken 51 times.
✗ Branch 1 not taken.
102 autoinc_col_max_value = field->get_max_int_value();
6142 }
6143 35474 found_col:
6144
4/4
✓ Branch 0 taken 1594 times.
✓ Branch 1 taken 302261 times.
✓ Branch 2 taken 1470 times.
✓ Branch 3 taken 124 times.
607710 if (innobase_is_v_fld(new_field)) {
6145 2940 ++num_v;
6146 }
6147
6148 607710 i++;
6149 }
6150
6151
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 44104 times.
88208 assert(heap);
6152
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 44104 times.
88208 assert(m_user_thd == m_prebuilt->trx->mysql_thd);
6153
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 44104 times.
88208 assert(!ha_alter_info->handler_ctx);
6154
6155
1/2
✓ Branch 0 taken 44104 times.
✗ Branch 1 not taken.
176416 ha_alter_info->handler_ctx = new (m_user_thd->mem_root)
6156 ha_innobase_inplace_ctx(m_prebuilt, drop_index, n_drop_index,
6157 rename_index, n_rename_index, drop_fk, n_drop_fk,
6158 88208 add_fk, n_add_fk, ha_alter_info->online, heap,
6159 88208 m_prebuilt->table, col_names, add_autoinc_col_no,
6160
1/2
✓ Branch 0 taken 44104 times.
✗ Branch 1 not taken.
88208 ha_alter_info->create_info->auto_increment_value,
6161 autoinc_col_max_value);
6162
6163 88208 return prepare_inplace_alter_table_dict(
6164 88208 ha_alter_info, altered_table, table, old_dd_tab, new_dd_tab,
6165
1/2
✓ Branch 0 taken 44010 times.
✗ Branch 1 not taken.
176416 table_share->table_name.str, info.flags(), info.flags2() | encrypt_flag,
6166 88020 fts_doc_col_no, add_fts_doc_id, add_fts_doc_id_idx, m_prebuilt);
6167 141974 }
6168
6169 /** Check that the column is part of a virtual index(index contains
6170 virtual column) in the table
6171 @param[in] table Table containing column
6172 @param[in] col column to be checked
6173 @return true if this column is indexed with other virtual columns */
6174 117 static bool dict_col_in_v_indexes(dict_table_t *table, dict_col_t *col) {
6175
2/2
✓ Branch 0 taken 234 times.
✓ Branch 1 taken 117 times.
351 for (dict_index_t *index = table->first_index()->next(); index != nullptr;
6176 234 index = index->next()) {
6177
1/2
✓ Branch 0 taken 234 times.
✗ Branch 1 not taken.
234 if (!dict_index_has_virtual(index)) {
6178 234 continue;
6179 }
6180 for (ulint k = 0; k < index->n_fields; k++) {
6181 dict_field_t *field = index->get_field(k);
6182 if (field->col->ind == col->ind) {
6183 return (true);
6184 }
6185 }
6186 }
6187
6188 117 return (false);
6189 }
6190
6191 /* Check whether a columnn length change alter operation requires
6192 to rebuild the template.
6193 @param[in] altered_table TABLE object for new version of table.
6194 @param[in] ha_alter_info Structure describing changes to be done
6195 by ALTER TABLE and holding data used
6196 during in-place alter.
6197 @param[in] table table being altered
6198 @return true if needs rebuild. */
6199 12 static bool alter_templ_needs_rebuild(TABLE *altered_table,
6200 Alter_inplace_info *ha_alter_info,
6201 dict_table_t *table) {
6202 12 ulint i = 0;
6203 List_iterator_fast<Create_field> cf_it(
6204
1/2
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
12 ha_alter_info->alter_info->create_list);
6205
6206
2/2
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 12 times.
28 for (Field **fp = altered_table->field; *fp; fp++, i++) {
6207 16 cf_it.rewind();
6208
2/2
✓ Branch 0 taken 26 times.
✓ Branch 1 taken 16 times.
42 while (const Create_field *cf = cf_it++) {
6209
2/2
✓ Branch 0 taken 130 times.
✓ Branch 1 taken 26 times.
156 for (ulint j = 0; j < table->n_cols; j++) {
6210
1/2
✓ Branch 0 taken 130 times.
✗ Branch 1 not taken.
130 dict_col_t *cols = table->get_col(j);
6211
4/6
✓ Branch 0 taken 130 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 117 times.
✓ Branch 3 taken 13 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 130 times.
247 if (cf->max_display_width_in_bytes() > cols->len &&
6212
2/4
✓ Branch 0 taken 117 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 117 times.
117 dict_col_in_v_indexes(table, cols)) {
6213 return (true);
6214 }
6215 }
6216 26 }
6217 }
6218
6219 12 return (false);
6220 }
6221
6222 /** Get the name of an erroneous key.
6223 @param[in] error_key_num InnoDB number of the erroneus key
6224 @param[in] ha_alter_info changes that were being performed
6225 @param[in] table InnoDB table
6226 @return the name of the erroneous key */
6227 2 static const char *get_error_key_name(ulint error_key_num,
6228 const Alter_inplace_info *ha_alter_info,
6229 const dict_table_t *table) {
6230
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (error_key_num == ULINT_UNDEFINED) {
6231 return (FTS_DOC_ID_INDEX_NAME);
6232
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 } else if (ha_alter_info->key_count == 0) {
6233 return (table->first_index()->name);
6234 } else {
6235 2 return (ha_alter_info->key_info_buffer[error_key_num].name);
6236 }
6237 }
6238
6239 template <typename Table>
6240 141544 bool ha_innobase::inplace_alter_table_impl(TABLE *altered_table,
6241 Alter_inplace_info *ha_alter_info) {
6242 141544 dict_add_v_col_t *add_v = nullptr;
6243 141544 dict_vcol_templ_t *s_templ = nullptr;
6244 141544 dict_vcol_templ_t *old_templ = nullptr;
6245 141544 struct TABLE *eval_table = altered_table;
6246 141544 bool rebuild_templ = false;
6247
1/2
✓ Branch 0 taken 70772 times.
✗ Branch 1 not taken.
141544 DBUG_TRACE;
6248
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 70772 times.
141544 assert(!srv_read_only_mode);
6249
6250
2/4
✓ Branch 0 taken 70772 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 70772 times.
141544 ut_ad(!rw_lock_own(dict_operation_lock, RW_LOCK_X));
6251
2/4
✓ Branch 0 taken 70772 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 70772 times.
141544 ut_ad(!rw_lock_own(dict_operation_lock, RW_LOCK_S));
6252
6253
3/4
✓ Branch 0 taken 69195 times.
✓ Branch 1 taken 1577 times.
✓ Branch 2 taken 69195 times.
✗ Branch 3 not taken.
141544 DEBUG_SYNC(m_user_thd, "innodb_inplace_alter_table_enter");
6254
6255 280465 auto all_ok = [=]() -> bool {
6256
3/4
✓ Branch 0 taken 8161 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 60511 times.
✓ Branch 3 taken 1577 times.
70249 DEBUG_SYNC(m_user_thd, "innodb_after_inplace_alter_table");
6257 70249 return false;
6258 };
6259
6260 271744 auto success = [&]() -> bool {
6261 43400 ut_d(dict_sys_mutex_enter());
6262 43400 ut_d(dict_table_check_for_dup_indexes(m_prebuilt->table, CHECK_PARTIAL_OK));
6263 43400 ut_d(dict_sys_mutex_exit());
6264 /* prebuilt->table->n_ref_count can be anything here,
6265 given that we hold at most a shared lock on the table. */
6266 43400 return all_ok();
6267 };
6268
6269
6/6
✓ Branch 0 taken 57883 times.
✓ Branch 1 taken 12889 times.
✓ Branch 2 taken 2030 times.
✓ Branch 3 taken 55853 times.
✓ Branch 4 taken 14919 times.
✓ Branch 5 taken 55853 times.
257310 if (!(ha_alter_info->handler_flags & INNOBASE_ALTER_DATA) ||
6270 115766 is_instant(ha_alter_info)) {
6271
1/2
✓ Branch 0 taken 14919 times.
✗ Branch 1 not taken.
29838 return all_ok();
6272 }
6273
6274 223412 if (((ha_alter_info->handler_flags & ~INNOBASE_INPLACE_IGNORE) ==
6275
4/4
✓ Branch 0 taken 24634 times.
✓ Branch 1 taken 31219 times.
✓ Branch 2 taken 11930 times.
✓ Branch 3 taken 43923 times.
160974 Alter_inplace_info::CHANGE_CREATE_OPTION &&
6276
2/2
✓ Branch 0 taken 11930 times.
✓ Branch 1 taken 12704 times.
49268 !innobase_need_rebuild(
6277 49268 ha_alter_info, table,
6278
1/2
✓ Branch 0 taken 24634 times.
✗ Branch 1 not taken.
49268 dict_table_is_file_per_table(m_prebuilt->table)))) {
6279
1/2
✓ Branch 0 taken 11930 times.
✗ Branch 1 not taken.
23860 return all_ok();
6280 }
6281
6282 87846 ha_innobase_inplace_ctx *ctx =
6283 87846 static_cast<ha_innobase_inplace_ctx *>(ha_alter_info->handler_ctx);
6284
6285
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 43923 times.
87846 assert(ctx);
6286
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 43923 times.
87846 assert(ctx->trx);
6287
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 43923 times.
87846 assert(ctx->prebuilt == m_prebuilt);
6288
6289
1/2
✓ Branch 0 taken 43923 times.
✗ Branch 1 not taken.
87846 dict_index_t *pk = m_prebuilt->table->first_index();
6290
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 43923 times.
87846 ut_ad(pk != nullptr);
6291
6292 /* For partitioned tables this could be already allocated from a
6293 previous partition invocation. For normal tables this is NULL. */
6294 87846 ut::delete_(ctx->m_stage);
6295
6296
1/2
✓ Branch 0 taken 43923 times.
✗ Branch 1 not taken.
87846 ctx->m_stage = ut::new_withkey<Alter_stage>(UT_NEW_THIS_FILE_PSI_KEY, pk);
6297
6298
5/6
✓ Branch 0 taken 43915 times.
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 43915 times.
✓ Branch 4 taken 8 times.
✓ Branch 5 taken 43915 times.
175676 if (m_prebuilt->table->ibd_file_missing ||
6299 87830 dict_table_is_discarded(m_prebuilt->table)) {
6300
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
16 return success();
6301 }
6302
6303 /* If we are doing a table rebuilding or having added virtual
6304 columns in the same clause, we will need to build a table template
6305 that carries translation information between MySQL TABLE and InnoDB
6306 table, which indicates the virtual columns and their base columns
6307 info. This is used to do the computation callback, so that the
6308 data in base columns can be extracted send to server.
6309 If the Column length changes and it is a part of virtual
6310 index then we need to rebuild the template. */
6311 87830 rebuild_templ =
6312
2/2
✓ Branch 0 taken 8153 times.
✓ Branch 1 taken 35762 times.
104136 ctx->need_rebuild() ||
6313
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 8141 times.
16306 ((ha_alter_info->handler_flags &
6314 24 Alter_inplace_info::ALTER_COLUMN_EQUAL_PACK_LENGTH) &&
6315
2/4
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 12 times.
24 alter_templ_needs_rebuild(altered_table, ha_alter_info, ctx->new_table));
6316
6317
4/4
✓ Branch 0 taken 1155 times.
✓ Branch 1 taken 42760 times.
✓ Branch 2 taken 900 times.
✓ Branch 3 taken 255 times.
87830 if ((ctx->new_table->n_v_cols > 0) && rebuild_templ) {
6318 /* Save the templ if isn't NULL so as to restore the
6319 original state in case of alter operation failures. */
6320
2/6
✗ Branch 0 not taken.
✓ Branch 1 taken 900 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 900 times.
1800 if (ctx->new_table->vc_templ != nullptr && !ctx->need_rebuild()) {
6321 old_templ = ctx->new_table->vc_templ;
6322 }
6323
1/2
✓ Branch 0 taken 900 times.
✗ Branch 1 not taken.
1800 s_templ = ut::new_withkey<dict_vcol_templ_t>(UT_NEW_THIS_FILE_PSI_KEY);
6324 1800 s_templ->vtempl = nullptr;
6325
6326
1/2
✓ Branch 0 taken 900 times.
✗ Branch 1 not taken.
1800 innobase_build_v_templ(altered_table, ctx->new_table, s_templ, nullptr,
6327 false, nullptr);
6328
6329 1800 ctx->new_table->vc_templ = s_templ;
6330
2/2
✓ Branch 0 taken 167 times.
✓ Branch 1 taken 42848 times.
86030 } else if (ha_alter_info->virtual_column_add_count > 0 &&
6331
2/2
✓ Branch 0 taken 166 times.
✓ Branch 1 taken 1 times.
334 ha_alter_info->virtual_column_drop_count == 0) {
6332 /* if there is ongoing drop virtual column, then we disallow
6333 inplace add index on newly added virtual column, so it does
6334 not need to come in here to rebuild template with add_v.
6335 Please also see the assertion in innodb_v_adjust_idx_col() */
6336
6337
1/2
✓ Branch 0 taken 166 times.
✗ Branch 1 not taken.
332 s_templ = ut::new_withkey<dict_vcol_templ_t>(UT_NEW_THIS_FILE_PSI_KEY);
6338
6339 add_v = static_cast<dict_add_v_col_t *>(
6340
1/2
✓ Branch 0 taken 166 times.
✗ Branch 1 not taken.
332 mem_heap_alloc(ctx->heap, sizeof *add_v));
6341 332 add_v->n_v_col = ha_alter_info->virtual_column_add_count;
6342 332 add_v->v_col = ctx->add_vcol;
6343 332 add_v->v_col_name = ctx->add_vcol_name;
6344
6345 332 s_templ->vtempl = nullptr;
6346
6347
1/2
✓ Branch 0 taken 166 times.
✗ Branch 1 not taken.
332 innobase_build_v_templ(altered_table, ctx->new_table, s_templ, add_v, false,
6348 nullptr);
6349 332 old_templ = ctx->new_table->vc_templ;
6350 332 ctx->new_table->vc_templ = s_templ;
6351 }
6352
6353 /* Drop virtual column without rebuild will keep dict table
6354 unchanged, we use old table to evaluate virtual column value
6355 in innobase_get_computed_value(). */
6356
6/6
✓ Branch 0 taken 8153 times.
✓ Branch 1 taken 35762 times.
✓ Branch 2 taken 8 times.
✓ Branch 3 taken 8145 times.
✓ Branch 4 taken 8 times.
✓ Branch 5 taken 43907 times.
87830 if (!ctx->need_rebuild() && ha_alter_info->virtual_column_drop_count > 0) {
6357 16 eval_table = table;
6358 }
6359
6360 347443 auto clean_up = [&](dberr_t err) -> bool {
6361
3/4
✓ Branch 0 taken 6637 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 36439 times.
✓ Branch 3 taken 839 times.
43915 DEBUG_SYNC_C("alter_table_update_log");
6362
6363
16/16
✓ Branch 0 taken 6635 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 6610 times.
✓ Branch 3 taken 25 times.
✓ Branch 4 taken 6018 times.
✓ Branch 5 taken 592 times.
✓ Branch 6 taken 6018 times.
✓ Branch 7 taken 619 times.
✓ Branch 8 taken 36767 times.
✓ Branch 9 taken 511 times.
✓ Branch 10 taken 35277 times.
✓ Branch 11 taken 1490 times.
✓ Branch 12 taken 29002 times.
✓ Branch 13 taken 6275 times.
✓ Branch 14 taken 29002 times.
✓ Branch 15 taken 8276 times.
43915 if (err == DB_SUCCESS && ctx->online && ctx->need_rebuild()) {
6364
3/4
✓ Branch 0 taken 6018 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 28163 times.
✓ Branch 3 taken 839 times.
35020 DEBUG_SYNC_C("row_log_table_apply1_before");
6365 35020 err = row_log_table_apply(ctx->thr, m_prebuilt->table, altered_table,
6366 ctx->m_stage);
6367 }
6368
6369
3/4
✗ Branch 0 not taken.
✓ Branch 1 taken 6637 times.
✓ Branch 2 taken 1066 times.
✓ Branch 3 taken 36212 times.
43915 if (s_templ) {
6370
4/16
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 166 times.
✓ Branch 9 taken 900 times.
✗ Branch 10 not taken.
✓ Branch 11 taken 166 times.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✓ Branch 15 taken 1066 times.
1066 ut_ad(ctx->need_rebuild() ||
6371 ha_alter_info->virtual_column_add_count > 0 || rebuild_templ);
6372 1066 dict_free_vc_templ(s_templ);
6373 1066 ut::delete_(s_templ);
6374
6375 1066 ctx->new_table->vc_templ = old_templ;
6376 }
6377
6378
3/4
✓ Branch 0 taken 6637 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 36439 times.
✓ Branch 3 taken 839 times.
43915 DEBUG_SYNC_C("inplace_after_index_build");
6379
6380
3/4
✗ Branch 0 not taken.
✓ Branch 1 taken 6637 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 37277 times.
43915 DBUG_EXECUTE_IF("create_index_fail", err = DB_DUPLICATE_KEY;
6381 m_prebuilt->trx->error_key_num = ULINT_UNDEFINED;);
6382
6383 /* After an error, remove all those index definitions
6384 from the dictionary which were defined. */
6385
6386
6/10
✓ Branch 0 taken 6635 times.
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 36757 times.
✓ Branch 6 taken 442 times.
✓ Branch 7 taken 2 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 77 times.
43915 switch (err) {
6387 43392 case DB_SUCCESS:
6388 43392 return success();
6389 444 case DB_DUPLICATE_KEY: {
6390 444 KEY *dup_key{};
6391
3/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 441 times.
✓ Branch 3 taken 1 times.
444 if (m_prebuilt->trx->error_key_num == ULINT_UNDEFINED ||
6392
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 441 times.
✗ Branch 3 not taken.
443 ha_alter_info->key_count == 0) {
6393 /* This should be the hidden index on
6394 FTS_DOC_ID, or there is no PRIMARY KEY in the
6395 table. Either way, we should be seeing and
6396 reporting a bogus duplicate key error. */
6397
4/4
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 27 times.
✓ Branch 3 taken 414 times.
443 } else if (m_prebuilt->trx->error_key_num == 0) {
6398 28 dup_key =
6399 28 &ha_alter_info->key_info_buffer[m_prebuilt->trx->error_key_num];
6400 } else {
6401 /* Check if there is generated cluster index column */
6402
3/4
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 412 times.
415 if (ctx->num_to_add_index > ha_alter_info->key_count) {
6403
1/4
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
2 assert(m_prebuilt->trx->error_key_num <= ha_alter_info->key_count);
6404 2 dup_key =
6405 &ha_alter_info
6406 2 ->key_info_buffer[m_prebuilt->trx->error_key_num - 1];
6407 } else {
6408
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 412 times.
413 assert(m_prebuilt->trx->error_key_num < ha_alter_info->key_count);
6409 413 dup_key =
6410 413 &ha_alter_info->key_info_buffer[m_prebuilt->trx->error_key_num];
6411 }
6412 }
6413 444 print_keydup_error(altered_table, dup_key, MYF(0),
6414 444 table_share->table_name.str);
6415 444 break;
6416 }
6417 2 case DB_ONLINE_LOG_TOO_BIG:
6418
1/4
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
2 assert(ctx->online);
6419 2 my_error(ER_INNODB_ONLINE_LOG_TOO_BIG, MYF(0),
6420 2 get_error_key_name(m_prebuilt->trx->error_key_num,
6421 2 ha_alter_info, m_prebuilt->table));
6422 2 break;
6423 case DB_INDEX_CORRUPT:
6424 my_error(ER_INDEX_CORRUPT, MYF(0),
6425 get_error_key_name(m_prebuilt->trx->error_key_num,
6426 ha_alter_info, m_prebuilt->table));
6427 break;
6428 77 default:
6429 77 my_error_innodb(err, table_share->table_name.str,
6430 77 m_prebuilt->table->flags);
6431 }
6432
6433 /* prebuilt->table->n_ref_count can be anything here, given
6434 that we hold at most a shared lock on the table. */
6435 523 m_prebuilt->trx->error_index = nullptr;
6436 523 ctx->trx->error_state = DB_SUCCESS;
6437
6438 523 return true;
6439 };
6440
6441 /* Read the clustered index of the table and build
6442 indexes based on this information using temporary
6443 files and merge sort. */
6444
4/6
✓ Branch 0 taken 43915 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 43913 times.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
87830 DBUG_EXECUTE_IF("innodb_OOM_inplace_alter",
6445 return clean_up(DB_OUT_OF_MEMORY););
6446
6447 87826 const auto trx = m_prebuilt->trx;
6448 87826 const auto old_isolation_level = trx->isolation_level;
6449
6450
2/2
✓ Branch 0 taken 42355 times.
✓ Branch 1 taken 1558 times.
87826 if (ctx->online &&
6451
2/2
✓ Branch 0 taken 53 times.
✓ Branch 1 taken 42302 times.
84710 trx->isolation_level != trx_t::isolation_level_t::REPEATABLE_READ) {
6452 /* We must scan the index at an isolation level >= READ COMMITTED, because
6453 a dirty read will see half written blob references.
6454
6455 ** Perform a REPEATABLE READ.
6456 When rebuilding the table online, row_log_table_apply() must not see
6457 a newer state of the table when applying the log. This is mainly to
6458 prevent false duplicate key errors, because the log will identify records
6459 by the PRIMARY KEY, and also to prevent unsafe BLOB access.
6460
6461 When creating a secondary index online, this table scan must not see
6462 records that have only been inserted to the clustered index, but have
6463 not been written to the online_log of index[]. If we performed
6464 READ UNCOMMITTED, it could happen that the ADD INDEX reaches
6465 ONLINE_INDEX_COMPLETE state between the time the DML thread has updated
6466 the clustered index but has not yet accessed secondary index. */
6467
6468 106 trx->isolation_level = trx_t::isolation_level_t::REPEATABLE_READ;
6469 }
6470
6471 263478 ddl::Context ddl(trx, m_prebuilt->table, ctx->new_table, ctx->online,
6472 ctx->add_index, ctx->add_key_numbers, ctx->num_to_add_index,
6473 87826 altered_table, ctx->add_cols, ctx->col_map, ctx->add_autoinc,
6474 87826 ctx->sequence, ctx->skip_pk_sort, ctx->m_stage, add_v,
6475
1/2
✓ Branch 0 taken 43913 times.
✗ Branch 1 not taken.
87826 eval_table, thd_ddl_buffer_size(m_prebuilt->trx->mysql_thd),
6476 87826 thd_ddl_threads(m_prebuilt->trx->mysql_thd));
6477
6478
1/2
✓ Branch 0 taken 43913 times.
✗ Branch 1 not taken.
87826 const auto err = clean_up(ddl.build());
6479
6480 87826 trx->isolation_level = old_isolation_level;
6481
6482 87826 return err;
6483 141544 }
6484
6485 /** Free the modification log for online table rebuild.
6486 @param table table that was being rebuilt online */
6487 35744 static void innobase_online_rebuild_log_free(dict_table_t *table) {
6488 35744 dict_index_t *clust_index = table->first_index();
6489
6490
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 35744 times.
35744 ut_ad(dict_sys_mutex_own());
6491
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 35744 times.
35744 ut_ad(rw_lock_own(dict_operation_lock, RW_LOCK_X));
6492
6493 35744 rw_lock_x_lock(&clust_index->lock, UT_LOCATION_HERE);
6494
6495
2/2
✓ Branch 0 taken 35038 times.
✓ Branch 1 taken 706 times.
35744 if (clust_index->online_log) {
6496
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 35038 times.
35038 ut_ad(dict_index_get_online_status(clust_index) == ONLINE_INDEX_CREATION);
6497 35038 clust_index->online_status = ONLINE_INDEX_COMPLETE;
6498 35038 row_log_free(clust_index->online_log);
6499
2/2
✓ Branch 0 taken 34199 times.
✓ Branch 1 taken 839 times.
35038 DEBUG_SYNC_C("innodb_online_rebuild_log_free_aborted");
6500 }
6501
6502
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 35744 times.
35744 assert(dict_index_get_online_status(clust_index) == ONLINE_INDEX_COMPLETE);
6503 35744 rw_lock_x_unlock(&clust_index->lock);
6504 35744 }
6505
6506 /** Rollback a secondary index creation, drop the indexes with
6507 temparary index prefix
6508 @param user_table InnoDB table
6509 @param table the TABLE
6510 @param locked true=table locked, false=may need to do a lazy drop
6511 @param trx the transaction
6512 */
6513 484 static void innobase_rollback_sec_index(dict_table_t *user_table,
6514 const TABLE *table, bool locked,
6515 trx_t *trx) {
6516 484 ddl::drop_indexes(trx, user_table, locked);
6517
6518 /* Free the table->fts only if there is no FTS_DOC_ID
6519 in the table */
6520 972 if (user_table->fts &&
6521
4/6
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 480 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 4 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 484 times.
484 !DICT_TF2_FLAG_IS_SET(user_table, DICT_TF2_FTS_HAS_DOC_ID) &&
6522 !innobase_fulltext_exist(table)) {
6523 fts_free(user_table);
6524 }
6525 484 }
6526
6527 /** Roll back the changes made during prepare_inplace_alter_table()
6528 and inplace_alter_table() inside the storage engine. Note that the
6529 allowed level of concurrency during this operation will be the same as
6530 for inplace_alter_table() and thus might be higher than during
6531 prepare_inplace_alter_table(). (E.g concurrent writes were blocked
6532 during prepare, but might not be during commit).
6533
6534 @param[in] ha_alter_info Data used during in-place alter.
6535 @param[in] table the TABLE
6536 @param[in,out] prebuilt the prebuilt struct
6537 @retval true Failure
6538 @retval false Success
6539 */
6540 737 [[nodiscard]] inline bool rollback_inplace_alter_table(
6541 const Alter_inplace_info *ha_alter_info, const TABLE *table,
6542 row_prebuilt_t *prebuilt) {
6543 737 bool fail = false;
6544
6545 737 ha_innobase_inplace_ctx *ctx =
6546 static_cast<ha_innobase_inplace_ctx *>(ha_alter_info->handler_ctx);
6547
6548
1/2
✓ Branch 0 taken 737 times.
✗ Branch 1 not taken.
737 DBUG_TRACE;
6549
6550
4/4
✓ Branch 0 taken 556 times.
✓ Branch 1 taken 181 times.
✓ Branch 2 taken 5 times.
✓ Branch 3 taken 551 times.
737 if (!ctx || !ctx->trx) {
6551 /* If we have not started a transaction yet,
6552 (almost) nothing has been or needs to be done. */
6553 186 goto func_exit;
6554 }
6555
6556
1/2
✓ Branch 0 taken 551 times.
✗ Branch 1 not taken.
551 row_mysql_lock_data_dictionary(ctx->trx, UT_LOCATION_HERE);
6557
6558
2/2
✓ Branch 0 taken 74 times.
✓ Branch 1 taken 477 times.
551 if (ctx->need_rebuild()) {
6559 /* The table could have been closed in commit phase */
6560
2/2
✓ Branch 0 taken 69 times.
✓ Branch 1 taken 5 times.
74 if (ctx->new_table != nullptr) {
6561 69 dberr_t err = DB_SUCCESS;
6562 69 uint32_t flags = ctx->new_table->flags;
6563 /* DML threads can access ctx->new_table via the
6564 online rebuild log. Free it first. */
6565
1/2
✓ Branch 0 taken 69 times.
✗ Branch 1 not taken.
69 innobase_online_rebuild_log_free(prebuilt->table);
6566
6567
1/2
✓ Branch 0 taken 69 times.
✗ Branch 1 not taken.
69 dict_table_close(ctx->new_table, true, false);
6568
6569
1/2
✓ Branch 0 taken 69 times.
✗ Branch 1 not taken.
69 switch (err) {
6570 69 case DB_SUCCESS:
6571 69 break;
6572 default:
6573 my_error_innodb(err, table->s->table_name.str, flags);
6574 fail = true;
6575 }
6576 }
6577 } else {
6578
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 477 times.
477 assert(!(ha_alter_info->handler_flags & Alter_inplace_info::ADD_PK_INDEX));
6579
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 477 times.
477 assert(ctx->new_table == prebuilt->table);
6580
6581
1/2
✓ Branch 0 taken 477 times.
✗ Branch 1 not taken.
477 innobase_rollback_sec_index(prebuilt->table, table, false, ctx->trx);
6582 }
6583
6584
1/2
✓ Branch 0 taken 551 times.
✗ Branch 1 not taken.
551 row_mysql_unlock_data_dictionary(ctx->trx);
6585
6586 737 func_exit:
6587 #ifdef UNIV_DEBUG
6588
1/2
✓ Branch 0 taken 737 times.
✗ Branch 1 not taken.
737 dict_index_t *clust_index = prebuilt->table->first_index();
6589
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 737 times.
737 assert(!clust_index->online_log);
6590
2/4
✓ Branch 0 taken 737 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 737 times.
737 assert(dict_index_get_online_status(clust_index) == ONLINE_INDEX_COMPLETE);
6591 #endif /* UNIV_DEBUG */
6592
6593
2/2
✓ Branch 0 taken 556 times.
✓ Branch 1 taken 181 times.
737 if (ctx) {
6594
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 556 times.
556 assert(ctx->prebuilt == prebuilt);
6595
6596
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 553 times.
556 if (ctx->num_to_add_fk) {
6597
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 3 times.
6 for (ulint i = 0; i < ctx->num_to_add_fk; i++) {
6598
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 dict_foreign_free(ctx->add_fk[i]);
6599 }
6600 }
6601
6602
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 552 times.
556 if (ctx->num_to_drop_index) {
6603
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 row_mysql_lock_data_dictionary(prebuilt->trx, UT_LOCATION_HERE);
6604
6605 /* Clear the to_be_dropped flags
6606 in the data dictionary cache.
6607 The flags may already have been cleared,
6608 in case an error was detected in
6609 commit_inplace_alter_table(). */
6610
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 4 times.
8 for (ulint i = 0; i < ctx->num_to_drop_index; i++) {
6611 4 dict_index_t *index = ctx->drop_index[i];
6612
2/4
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 4 times.
4 assert(index->is_committed());
6613 4 index->to_be_dropped = 0;
6614 }
6615
6616
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 row_mysql_unlock_data_dictionary(prebuilt->trx);
6617 }
6618 }
6619
6620
1/2
✓ Branch 0 taken 737 times.
✗ Branch 1 not taken.
737 reset_column_ord_part(prebuilt->table);
6621
6622 /* Do not commit/rollback prebuilt->trx, assume mysql will
6623 rollback it */
6624
6625 737 MONITOR_ATOMIC_DEC(MONITOR_PENDING_ALTER_TABLE);
6626 737 return fail;
6627 737 }
6628
6629 /** Rename or enlarge columns in the data dictionary cache
6630 as part of commit_cache_norebuild().
6631 @param ha_alter_info Data used during in-place alter.
6632 @param table the TABLE
6633 @param user_table InnoDB table that was being altered */
6634 23544 static void innobase_rename_or_enlarge_columns_cache(
6635 Alter_inplace_info *ha_alter_info, const TABLE *table,
6636 dict_table_t *user_table) {
6637
2/2
✓ Branch 0 taken 23345 times.
✓ Branch 1 taken 199 times.
23544 if (!(ha_alter_info->handler_flags &
6638 (Alter_inplace_info::ALTER_COLUMN_EQUAL_PACK_LENGTH |
6639 Alter_inplace_info::ALTER_COLUMN_NAME))) {
6640 23345 return;
6641 }
6642
6643 List_iterator_fast<Create_field> cf_it(
6644
1/2
✓ Branch 0 taken 199 times.
✗ Branch 1 not taken.
199 ha_alter_info->alter_info->create_list);
6645 199 uint i = 0;
6646 199 ulint num_v = 0;
6647 199 ulint unsigned_flag = 0;
6648
6649
2/2
✓ Branch 0 taken 497 times.
✓ Branch 1 taken 199 times.
696 for (Field **fp = table->field; *fp; fp++, i++) {
6650
3/4
✓ Branch 0 taken 51 times.
✓ Branch 1 taken 446 times.
✓ Branch 2 taken 51 times.
✗ Branch 3 not taken.
497 bool is_virtual = innobase_is_v_fld(*fp);
6651
6652 497 cf_it.rewind();
6653
1/2
✓ Branch 0 taken 1617 times.
✗ Branch 1 not taken.
1617 while (const Create_field *cf = cf_it++) {
6654
2/2
✓ Branch 0 taken 1120 times.
✓ Branch 1 taken 497 times.
1617 if (cf->field != *fp) {
6655 1120 continue;
6656 }
6657
6658
2/2
✓ Branch 0 taken 51 times.
✓ Branch 1 taken 446 times.
497 ulint col_n = is_virtual ? num_v : i - num_v;
6659
6660
3/4
✓ Branch 0 taken 497 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 146 times.
✓ Branch 3 taken 351 times.
497 if ((*fp)->is_equal(cf) == IS_EQUAL_PACK_LENGTH) {
6661 dict_col_t *col;
6662
6663
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 145 times.
146 if (is_virtual) {
6664
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 col = &dict_table_get_nth_v_col(user_table, col_n)->m_col;
6665 } else {
6666
1/2
✓ Branch 0 taken 145 times.
✗ Branch 1 not taken.
145 col = user_table->get_col(col_n);
6667 }
6668
1/2
✓ Branch 0 taken 146 times.
✗ Branch 1 not taken.
146 col->len = cf->max_display_width_in_bytes();
6669
6670 ulint innodb_data_type =
6671
1/2
✓ Branch 0 taken 146 times.
✗ Branch 1 not taken.
146 get_innobase_type_from_mysql_type(&unsigned_flag, cf->field);
6672
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 146 times.
146 ut_ad(innodb_data_type != DATA_MISSING);
6673
6674
5/6
✓ Branch 0 taken 146 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 144 times.
✓ Branch 3 taken 2 times.
✓ Branch 4 taken 80 times.
✓ Branch 5 taken 66 times.
290 if (dtype_is_non_binary_string_type(innodb_data_type, col->prtype) &&
6675
3/4
✓ Branch 0 taken 144 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 80 times.
✓ Branch 3 taken 64 times.
144 (*fp)->charset()->number != cf->charset->number) {
6676
1/2
✓ Branch 0 taken 80 times.
✗ Branch 1 not taken.
80 ulint old_charset = (*fp)->charset()->number;
6677 80 ulint new_charset = cf->charset->number;
6678
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 80 times.
80 ut_ad(dtype_get_charset_coll(col->prtype) == old_charset);
6679
6680 80 col->prtype =
6681
1/2
✓ Branch 0 taken 80 times.
✗ Branch 1 not taken.
80 dtype_form_prtype(col->prtype - (old_charset << 16), new_charset);
6682 ulint mbminlen;
6683 ulint mbmaxlen;
6684
6685
1/2
✓ Branch 0 taken 80 times.
✗ Branch 1 not taken.
80 dtype_get_mblen(col->mtype, col->prtype, &mbminlen, &mbmaxlen);
6686 80 col->mbminmaxlen = DATA_MBMINMAXLEN(mbminlen, mbmaxlen);
6687 }
6688 }
6689
6690
2/2
✓ Branch 0 taken 64 times.
✓ Branch 1 taken 433 times.
497 if ((*fp)->is_flag_set(FIELD_IS_RENAMED)) {
6691 64 dict_mem_table_col_rename(user_table, col_n, cf->field->field_name,
6692
1/2
✓ Branch 0 taken 64 times.
✗ Branch 1 not taken.
64 cf->field_name, is_virtual);
6693 }
6694
6695 497 break;
6696 1120 }
6697
6698
2/2
✓ Branch 0 taken 51 times.
✓ Branch 1 taken 446 times.
497 if (is_virtual) {
6699 51 num_v++;
6700 }
6701 }
6702 }
6703 /** Get the auto-increment value of the table on commit.
6704 @param[in] ha_alter_info Data used during in-place alter
6705 @param[in,out] ctx In-place ALTER TABLE context
6706 return autoinc value in ctx->max_autoinc
6707 @param[in] altered_table MySQL table that is being altered
6708 @param[in] old_table MySQL table as it is before the ALTER operation
6709 @retval true Failure
6710 @retval false Success*/
6711 59264 [[nodiscard]] static bool commit_get_autoinc(Alter_inplace_info *ha_alter_info,
6712 ha_innobase_inplace_ctx *ctx,
6713 const TABLE *altered_table,
6714 const TABLE *old_table) {
6715
1/2
✓ Branch 0 taken 59264 times.
✗ Branch 1 not taken.
59264 DBUG_TRACE;
6716
6717
2/2
✓ Branch 0 taken 54778 times.
✓ Branch 1 taken 4486 times.
59264 if (!altered_table->found_next_number_field) {
6718 /* There is no AUTO_INCREMENT column in the table
6719 after the ALTER operation. */
6720 54778 ctx->max_autoinc = 0;
6721
2/2
✓ Branch 0 taken 51 times.
✓ Branch 1 taken 4435 times.
4486 } else if (ctx->add_autoinc != ULINT_UNDEFINED) {
6722 /* An AUTO_INCREMENT column was added. Get the last
6723 value from the sequence, which may be based on a
6724 supplied AUTO_INCREMENT value. */
6725 51 ctx->max_autoinc = ctx->sequence.last();
6726
2/2
✓ Branch 0 taken 1211 times.
✓ Branch 1 taken 3224 times.
4435 } else if ((ha_alter_info->handler_flags &
6727 1211 Alter_inplace_info::CHANGE_CREATE_OPTION) &&
6728
2/2
✓ Branch 0 taken 154 times.
✓ Branch 1 taken 1057 times.
1211 (ha_alter_info->create_info->used_fields & HA_CREATE_USED_AUTO)) {
6729 /* Check if the table is discarded */
6730
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 154 times.
154 if (dict_table_is_discarded(ctx->old_table)) {
6731 return true;
6732 }
6733
6734 /* An AUTO_INCREMENT value was supplied, but the table was not
6735 rebuilt. Get the user-supplied value or the last value from the
6736 sequence. */
6737 uint64_t max_value_table;
6738
6739 154 Field *autoinc_field = old_table->found_next_number_field;
6740
6741 154 ctx->max_autoinc = ha_alter_info->create_info->auto_increment_value;
6742
6743
1/2
✓ Branch 0 taken 154 times.
✗ Branch 1 not taken.
154 dict_table_autoinc_lock(ctx->old_table);
6744
6745 154 max_value_table = ctx->old_table->autoinc_persisted;
6746
6747 /* We still have to search the index here when we want to
6748 set the AUTO_INCREMENT value to a smaller or equal one.
6749
6750 Here is an example:
6751 Let's say we have a table t1 with one AUTOINC column, existing
6752 rows (1), (2), (100), (200), (1000), after following SQLs:
6753 DELETE FROM t1 WHERE a > 200;
6754 ALTER TABLE t1 AUTO_INCREMENT = 150;
6755 we expect the next value allocated from 201, but not 150.
6756
6757 We could only search the tree to know current max counter
6758 in the table and compare. */
6759
2/2
✓ Branch 0 taken 13 times.
✓ Branch 1 taken 141 times.
154 if (ctx->max_autoinc <= max_value_table) {
6760 dberr_t err;
6761 dict_index_t *index;
6762
6763
1/2
✓ Branch 0 taken 13 times.
✗ Branch 1 not taken.
13 index = dict_table_get_index_on_first_col(ctx->old_table,
6764 13 autoinc_field->field_index());
6765
6766
1/2
✓ Branch 0 taken 13 times.
✗ Branch 1 not taken.
13 err = row_search_max_autoinc(index, autoinc_field->field_name,
6767 &max_value_table);
6768
6769
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 13 times.
13 if (err != DB_SUCCESS) {
6770 ctx->max_autoinc = 0;
6771 ut_d(ut_error);
6772
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 6 times.
13 } else if (ctx->max_autoinc <= max_value_table) {
6773 ulonglong col_max_value;
6774 ulonglong offset;
6775
6776
1/2
✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
7 col_max_value = autoinc_field->get_max_int_value();
6777 7 offset = ctx->prebuilt->autoinc_offset;
6778 7 ctx->max_autoinc =
6779
1/2
✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
7 innobase_next_autoinc(max_value_table, 1, 1, offset, col_max_value);
6780 }
6781 }
6782
6783
1/2
✓ Branch 0 taken 154 times.
✗ Branch 1 not taken.
154 dict_table_autoinc_unlock(ctx->old_table);
6784 154 } else {
6785 /* An AUTO_INCREMENT value was not specified.
6786 Read the old counter value from the table. */
6787
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4281 times.
4281 ut_ad(old_table->found_next_number_field);
6788
1/2
✓ Branch 0 taken 4281 times.
✗ Branch 1 not taken.
4281 dict_table_autoinc_lock(ctx->old_table);
6789 4281 ctx->max_autoinc = ctx->old_table->autoinc;
6790
1/2
✓ Branch 0 taken 4281 times.
✗ Branch 1 not taken.
4281 dict_table_autoinc_unlock(ctx->old_table);
6791 }
6792
6793 59264 return false;
6794 59264 }
6795
6796 /** Add or drop foreign key constraints to the data dictionary tables,
6797 but do not touch the data dictionary cache.
6798 @param ctx In-place ALTER TABLE context
6799 @param trx Data dictionary transaction
6800 @param table_name Table name in MySQL
6801 @retval true Failure
6802 @retval false Success
6803 */
6804 59263 [[nodiscard]] static bool innobase_update_foreign_try(
6805 ha_innobase_inplace_ctx *ctx, trx_t *trx, const char *table_name) {
6806 ulint foreign_id;
6807 ulint i;
6808
6809
1/2
✓ Branch 0 taken 59263 times.
✗ Branch 1 not taken.
59263 DBUG_TRACE;
6810
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 59263 times.
59263 assert(ctx);
6811
6812
1/2
✓ Branch 0 taken 59263 times.
✗ Branch 1 not taken.
59263 foreign_id = dict_table_get_highest_foreign_id(ctx->new_table);
6813
6814 59263 foreign_id++;
6815
6816
2/2
✓ Branch 0 taken 132 times.
✓ Branch 1 taken 59261 times.
59393 for (i = 0; i < ctx->num_to_add_fk; i++) {
6817 132 dict_foreign_t *fk = ctx->add_fk[i];
6818
6819
4/6
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 123 times.
✓ Branch 2 taken 9 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 132 times.
132 ut_ad(fk->foreign_table == ctx->new_table ||
6820 fk->foreign_table == ctx->old_table);
6821
6822 264 dberr_t error = dict_create_add_foreign_id(&foreign_id,
6823
1/2
✓ Branch 0 taken 132 times.
✗ Branch 1 not taken.
132 ctx->old_table->name.m_name, fk);
6824
6825
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 132 times.
132 if (error != DB_SUCCESS) {
6826 my_error(ER_TOO_LONG_IDENT, MYF(0), fk->id);
6827 return true;
6828 }
6829
2/2
✓ Branch 0 taken 55 times.
✓ Branch 1 taken 77 times.
132 if (!fk->foreign_index) {
6830 110 fk->foreign_index = dict_foreign_find_index(
6831 55 ctx->new_table, ctx->col_names, fk->foreign_col_names, fk->n_fields,
6832 55 fk->referenced_index, true,
6833
1/2
✓ Branch 0 taken 55 times.
✗ Branch 1 not taken.
55 fk->type & (DICT_FOREIGN_ON_DELETE_SET_NULL |
6834 DICT_FOREIGN_ON_UPDATE_SET_NULL));
6835
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 55 times.
55 if (!fk->foreign_index) {
6836 my_error(ER_FK_INCORRECT_OPTION, MYF(0), table_name, fk->id);
6837 return true;
6838 }
6839 }
6840
6841 /* During upgrade, inserts into SYS_* should be avoided. */
6842
1/2
✓ Branch 0 taken 132 times.
✗ Branch 1 not taken.
132 if (!srv_is_upgrade_mode) {
6843
3/4
✓ Branch 0 taken 132 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 130 times.
132 DBUG_EXECUTE_IF("innodb_test_cannot_add_fk_system", error = DB_ERROR;);
6844
6845
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 130 times.
132 if (error != DB_SUCCESS) {
6846
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 my_error(ER_FK_FAIL_ADD_SYSTEM, MYF(0), fk->id);
6847 2 return true;
6848 }
6849 }
6850 }
6851
4/6
✓ Branch 0 taken 59261 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 59260 times.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
59261 DBUG_EXECUTE_IF("ib_drop_foreign_error",
6852 my_error_innodb(DB_OUT_OF_FILE_SPACE, table_name, 0);
6853 trx->error_state = DB_SUCCESS; return true;);
6854 59260 return false;
6855 59263 }
6856
6857 /** Update the foreign key constraint definitions in the data dictionary cache
6858 after the changes to data dictionary tables were committed.
6859 @param[in,out] ctx In-place ALTER TABLE context
6860 @param[in] user_thd MySQL connection
6861 @param[in,out] dd_table dd table instance
6862 @return InnoDB error code (should always be DB_SUCCESS) */
6863 23554 [[nodiscard]] static dberr_t innobase_update_foreign_cache(
6864 ha_innobase_inplace_ctx *ctx, THD *user_thd, dd::Table *dd_table) {
6865 dict_table_t *user_table;
6866 23554 dberr_t err = DB_SUCCESS;
6867
6868
1/2
✓ Branch 0 taken 23554 times.
✗ Branch 1 not taken.
23554 DBUG_TRACE;
6869
6870
2/4
✓ Branch 0 taken 23554 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 23554 times.
23554 ut_ad(dict_sys_mutex_own());
6871
6872 23554 user_table = ctx->old_table;
6873
6874 /* Discard the added foreign keys, because we will
6875 load them from the data dictionary. */
6876
2/2
✓ Branch 0 taken 122 times.
✓ Branch 1 taken 23554 times.
23676 for (ulint i = 0; i < ctx->num_to_add_fk; i++) {
6877 122 dict_foreign_t *fk = ctx->add_fk[i];
6878
1/2
✓ Branch 0 taken 122 times.
✗ Branch 1 not taken.
122 dict_foreign_free(fk);
6879 }
6880
6881
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23554 times.
23554 if (ctx->need_rebuild()) {
6882 /* The rebuilt table is already using the renamed
6883 column names. No need to pass col_names or to drop
6884 constraints from the data dictionary cache. */
6885 assert(!ctx->col_names);
6886 assert(user_table->foreign_set.empty());
6887 assert(user_table->referenced_set.empty());
6888 user_table = ctx->new_table;
6889 } else {
6890 /* Drop the foreign key constraints if the
6891 table was not rebuilt. If the table is rebuilt,
6892 there would not be any foreign key contraints for
6893 it yet in the data dictionary cache. */
6894
2/2
✓ Branch 0 taken 56 times.
✓ Branch 1 taken 23554 times.
23610 for (ulint i = 0; i < ctx->num_to_drop_fk; i++) {
6895 56 dict_foreign_t *fk = ctx->drop_fk[i];
6896
1/2
✓ Branch 0 taken 56 times.
✗ Branch 1 not taken.
56 dict_foreign_remove_from_cache(fk);
6897 }
6898 }
6899
6900 /* Load the old or added foreign keys from the data dictionary
6901 and prevent the table from being evicted from the data
6902 dictionary cache (work around the lack of WL#6049). */
6903
1/2
✓ Branch 0 taken 23554 times.
✗ Branch 1 not taken.
23554 dict_names_t fk_tables;
6904
6905
1/2
✓ Branch 0 taken 23554 times.
✗ Branch 1 not taken.
23554 dd::cache::Dictionary_client *client = dd::get_dd_client(user_thd);
6906
1/2
✓ Branch 0 taken 23554 times.
✗ Branch 1 not taken.
23554 dd::cache::Dictionary_client::Auto_releaser releaser(client);
6907 err =
6908
1/2
✓ Branch 0 taken 23554 times.
✗ Branch 1 not taken.
23554 dd_table_load_fk(client, user_table->name.m_name, ctx->col_names,
6909 user_table, dd_table, user_thd, true, true, &fk_tables);
6910
6911
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23554 times.
23554 if (err == DB_CANNOT_ADD_CONSTRAINT) {
6912 fk_tables.clear();
6913
6914 /* It is possible there are existing foreign key are
6915 loaded with "foreign_key checks" off,
6916 so let's retry the loading with charset_check is off */
6917 err = dd_table_load_fk(client, user_table->name.m_name, ctx->col_names,
6918 user_table, dd_table, user_thd, true, false,
6919 &fk_tables);
6920
6921 /* The load with "charset_check" off is successful, warn
6922 the user that the foreign key has loaded with mis-matched
6923 charset */
6924 if (err == DB_SUCCESS) {
6925 push_warning_printf(user_thd, Sql_condition::SL_WARNING, ER_ALTER_INFO,
6926 "Foreign key constraints for table '%s'"
6927 " are loaded with charset check off",
6928 user_table->name.m_name);
6929 }
6930 }
6931
6932 /* For complete loading of foreign keys, all associated tables must
6933 also be loaded. */
6934
6935
3/6
✓ Branch 0 taken 23554 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 23554 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 23554 times.
23554 while (err == DB_SUCCESS && !fk_tables.empty()) {
6936 dict_sys_mutex_exit();
6937 dd::cache::Dictionary_client *client = dd::get_dd_client(user_thd);
6938
6939 dd::cache::Dictionary_client::Auto_releaser releaser(client);
6940
6941 dd_open_fk_tables(fk_tables, false, user_thd);
6942 dict_sys_mutex_enter();
6943 }
6944
6945 23554 return err;
6946 23554 }
6947
6948 /** Discard the foreign key cache if anyone is affected by current
6949 column rename. This is only used for rebuild case.
6950 @param[in] ha_alter_info data used during in-place alter
6951 @param[in] mysql_table MySQL TABLE object
6952 @param[in,out] old_table InnoDB table object for old table */
6953 32 static void innobase_rename_col_discard_foreign(
6954 Alter_inplace_info *ha_alter_info, const TABLE *mysql_table,
6955 dict_table_t *old_table) {
6956 List_iterator_fast<Create_field> cf_it(
6957
1/2
✓ Branch 0 taken 32 times.
✗ Branch 1 not taken.
32 ha_alter_info->alter_info->create_list);
6958
6959
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 32 times.
32 ut_ad(ha_alter_info->handler_flags & Alter_inplace_info::ALTER_COLUMN_NAME);
6960
6961
2/2
✓ Branch 0 taken 108 times.
✓ Branch 1 taken 32 times.
140 for (Field **fp = mysql_table->field; *fp; fp++) {
6962
2/2
✓ Branch 0 taken 73 times.
✓ Branch 1 taken 35 times.
108 if (!(*fp)->is_flag_set(FIELD_IS_RENAMED)) {
6963 73 continue;
6964 }
6965
6966 35 cf_it.rewind();
6967
6968 35 ut_d(bool processed = false;)
6969
6970
2/2
✓ Branch 0 taken 127 times.
✓ Branch 1 taken 35 times.
162 while (Create_field *cf = cf_it++) {
6971
2/2
✓ Branch 0 taken 92 times.
✓ Branch 1 taken 35 times.
127 if (cf->field != *fp) {
6972 92 continue;
6973 }
6974
6975 /* Now cf->field->field_name is the old name, check the foreign key
6976 information to see any one gets affected by this rename, and discard
6977 them from cache */
6978
6979 35 std::list<dict_foreign_t *> fk_evict;
6980
6981
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 35 times.
44 for (auto fk : old_table->foreign_set) {
6982 9 dict_foreign_t *foreign = fk;
6983
6984
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 6 times.
15 for (unsigned i = 0; i < foreign->n_fields; i++) {
6985
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 3 times.
9 if (strcmp(foreign->foreign_col_names[i], cf->field->field_name) !=
6986 0) {
6987 6 continue;
6988 }
6989
6990
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 fk_evict.push_back(foreign);
6991 3 break;
6992 }
6993 }
6994
6995
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 35 times.
37 for (auto fk : old_table->referenced_set) {
6996 2 dict_foreign_t *foreign = fk;
6997
6998
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 for (unsigned i = 0; i < foreign->n_fields; i++) {
6999
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (strcmp(foreign->referenced_col_names[i], cf->field->field_name) !=
7000 0) {
7001 continue;
7002 }
7003
7004
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 fk_evict.push_back(foreign);
7005 2 break;
7006 }
7007 }
7008
7009
1/2
✓ Branch 0 taken 35 times.
✗ Branch 1 not taken.
35 std::for_each(fk_evict.begin(), fk_evict.end(),
7010 dict_foreign_remove_from_cache);
7011
7012 35 ut_d(processed = true;)
7013 162 }
7014
7015
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 35 times.
35 ut_ad(processed);
7016 }
7017 32 }
7018
7019 /** Commit the changes made during prepare_inplace_alter_table()
7020 and inplace_alter_table() inside the data dictionary tables,
7021 when rebuilding the table.
7022 @param ha_alter_info Data used during in-place alter
7023 @param ctx In-place ALTER TABLE context
7024 @param altered_table MySQL table that is being altered
7025 @param old_table MySQL table as it is before the ALTER operation
7026 @param trx Data dictionary transaction
7027 @param table_name Table name in MySQL
7028 @retval true Failure
7029 @retval false Success
7030 */
7031 35697 [[nodiscard]] inline bool commit_try_rebuild(Alter_inplace_info *ha_alter_info,
7032 ha_innobase_inplace_ctx *ctx,
7033 TABLE *altered_table,
7034 const TABLE *old_table, trx_t *trx,
7035 const char *table_name) {
7036 35697 dict_table_t *rebuilt_table = ctx->new_table;
7037 35697 dict_table_t *user_table = ctx->old_table;
7038
7039
1/2
✓ Branch 0 taken 35697 times.
✗ Branch 1 not taken.
35697 DBUG_TRACE;
7040
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 35697 times.
35697 assert(ctx->need_rebuild());
7041
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 35697 times.
35697 assert(trx->dict_operation_lock_mode == RW_X_LATCH);
7042
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 35697 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
35697 assert(
7043 !(ha_alter_info->handler_flags & Alter_inplace_info::DROP_FOREIGN_KEY) ||
7044 ctx->num_to_drop_fk > 0);
7045
7046
3/4
✓ Branch 0 taken 35697 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 46681 times.
✓ Branch 3 taken 35697 times.
82378 for (dict_index_t *index = rebuilt_table->first_index(); index;
7047
1/2
✓ Branch 0 taken 46681 times.
✗ Branch 1 not taken.
46681 index = index->next()) {
7048
2/4
✓ Branch 0 taken 46681 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 46681 times.
46681 assert(dict_index_get_online_status(index) == ONLINE_INDEX_COMPLETE);
7049
2/4
✓ Branch 0 taken 46681 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 46681 times.
46681 assert(index->is_committed());
7050
2/4
✓ Branch 0 taken 46681 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 46681 times.
46681 if (index->is_corrupted()) {
7051 my_error(ER_INDEX_CORRUPT, MYF(0), index->name());
7052 return true;
7053 }
7054 }
7055
7056
3/4
✓ Branch 0 taken 35697 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 35696 times.
35697 if (innobase_update_foreign_try(ctx, trx, table_name)) {
7057 1 return true;
7058 }
7059
7060 35696 dberr_t error = DB_SUCCESS;
7061
7062 /* Clear the to_be_dropped flag in the data dictionary cache
7063 of user_table. */
7064
2/2
✓ Branch 0 taken 80 times.
✓ Branch 1 taken 35696 times.
35776 for (ulint i = 0; i < ctx->num_to_drop_index; i++) {
7065 80 dict_index_t *index = ctx->drop_index[i];
7066
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 80 times.
80 assert(index->table == user_table);
7067
2/4
✓ Branch 0 taken 80 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 80 times.
80 assert(index->is_committed());
7068
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 80 times.
80 assert(index->to_be_dropped);
7069 80 index->to_be_dropped = 0;
7070 }
7071
7072 /* We copied the table. Any indexes that were requested to be
7073 dropped were not created in the copy of the table. Apply any
7074 last bit of the rebuild log and then rename the tables. */
7075
7076
2/2
✓ Branch 0 taken 35002 times.
✓ Branch 1 taken 694 times.
35696 if (ctx->online) {
7077
3/4
✓ Branch 0 taken 34163 times.
✓ Branch 1 taken 839 times.
✓ Branch 2 taken 34163 times.
✗ Branch 3 not taken.
35002 DEBUG_SYNC_C("row_log_table_apply2_before");
7078
7079 35002 dict_vcol_templ_t *s_templ = nullptr;
7080
7081
2/2
✓ Branch 0 taken 897 times.
✓ Branch 1 taken 34105 times.
35002 if (ctx->new_table->n_v_cols > 0) {
7082
1/2
✓ Branch 0 taken 897 times.
✗ Branch 1 not taken.
897 s_templ = ut::new_withkey<dict_vcol_templ_t>(UT_NEW_THIS_FILE_PSI_KEY);
7083 897 s_templ->vtempl = nullptr;
7084
7085
1/2
✓ Branch 0 taken 897 times.
✗ Branch 1 not taken.
897 innobase_build_v_templ(altered_table, ctx->new_table, s_templ, nullptr,
7086 true, nullptr);
7087 897 ctx->new_table->vc_templ = s_templ;
7088 }
7089
7090 70004 error = row_log_table_apply(
7091 ctx->thr, user_table, altered_table,
7092
1/2
✓ Branch 0 taken 35002 times.
✗ Branch 1 not taken.
35002 static_cast<ha_innobase_inplace_ctx *>(ha_alter_info->handler_ctx)
7093 ->m_stage);
7094
7095
2/2
✓ Branch 0 taken 897 times.
✓ Branch 1 taken 34105 times.
35002 if (s_templ) {
7096
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 897 times.
897 ut_ad(ctx->need_rebuild());
7097
1/2
✓ Branch 0 taken 897 times.
✗ Branch 1 not taken.
897 dict_free_vc_templ(s_templ);
7098 897 ut::delete_(s_templ);
7099 897 ctx->new_table->vc_templ = nullptr;
7100 }
7101
7102
1/2
✓ Branch 0 taken 35002 times.
✗ Branch 1 not taken.
35002 ulint err_key = thr_get_trx(ctx->thr)->error_key_num;
7103
7104
1/5
✓ Branch 0 taken 35002 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
35002 switch (error) {
7105 KEY *dup_key;
7106 35002 case DB_SUCCESS:
7107 35002 break;
7108 case DB_DUPLICATE_KEY:
7109 if (err_key == ULINT_UNDEFINED) {
7110 /* This should be the hidden index on
7111 FTS_DOC_ID. */
7112 dup_key = nullptr;
7113 } else {
7114 /* Check if there is generated cluster index column */
7115 if (ctx->num_to_add_index > ha_alter_info->key_count) {
7116 assert(err_key <= ha_alter_info->key_count);
7117 dup_key = &ha_alter_info->key_info_buffer[err_key - 1];
7118 } else {
7119 assert(err_key < ha_alter_info->key_count);
7120 dup_key = &ha_alter_info->key_info_buffer[err_key];
7121 }
7122 }
7123 print_keydup_error(altered_table, dup_key, MYF(0),
7124 old_table->s->table_name.str);
7125 return true;
7126 case DB_ONLINE_LOG_TOO_BIG:
7127 my_error(ER_INNODB_ONLINE_LOG_TOO_BIG, MYF(0),
7128 get_error_key_name(err_key, ha_alter_info, rebuilt_table));
7129 return true;
7130 case DB_INDEX_CORRUPT:
7131 my_error(ER_INDEX_CORRUPT, MYF(0),
7132 get_error_key_name(err_key, ha_alter_info, rebuilt_table));
7133 return true;
7134 default:
7135 my_error_innodb(error, table_name, user_table->flags);
7136 return true;
7137 }
7138 }
7139
2/6
✓ Branch 0 taken 35696 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 35696 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
35696 DBUG_EXECUTE_IF("ib_rename_column_error",
7140 my_error_innodb(DB_OUT_OF_FILE_SPACE, table_name, 0);
7141 trx->error_state = DB_SUCCESS; trx->op_info = "";
7142 return true;);
7143
2/6
✓ Branch 0 taken 35696 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 35696 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
35696 DBUG_EXECUTE_IF("ib_ddl_crash_before_rename", DBUG_SUICIDE(););
7144
7145 /* The new table must inherit the flag from the
7146 "parent" table. */
7147
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 35692 times.
35696 if (dict_table_is_discarded(user_table)) {
7148 4 rebuilt_table->ibd_file_missing = true;
7149 4 rebuilt_table->flags2 |= DICT_TF2_DISCARDED;
7150 }
7151 /* We must be still holding a table handle. */
7152
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 35696 times.
35696 assert(user_table->get_ref_count() >= 1);
7153
7154
2/6
✓ Branch 0 taken 35696 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 35696 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
35696 DBUG_EXECUTE_IF("ib_ddl_crash_after_rename", DBUG_SUICIDE(););
7155
3/4
✓ Branch 0 taken 35696 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 35695 times.
35696 DBUG_EXECUTE_IF("ib_rebuild_cannot_rename", error = DB_ERROR;);
7156
7157
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 35696 times.
35696 if (user_table->get_ref_count() > 1) {
7158 /* This should only occur when an innodb_memcached
7159 connection with innodb_api_enable_mdl=off was started
7160 before commit_inplace_alter_table() locked the data
7161 dictionary. We must roll back the ALTER TABLE, because
7162 we cannot drop a table while it is being used. */
7163
7164 /* Normally, n_ref_count must be 1, because purge
7165 cannot be executing on this very table as we are
7166 holding MDL lock. */
7167 my_error(ER_TABLE_REFERENCED, MYF(0));
7168 return true;
7169 }
7170
7171
2/4
✓ Branch 0 taken 35695 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
35696 switch (error) {
7172 35695 case DB_SUCCESS:
7173 35695 return false;
7174 case DB_TABLESPACE_EXISTS:
7175 ut_a(rebuilt_table->get_ref_count() == 1);
7176 my_error(ER_TABLESPACE_EXISTS, MYF(0), ctx->tmp_name);
7177 return true;
7178 case DB_DUPLICATE_KEY:
7179 ut_a(rebuilt_table->get_ref_count() == 1);
7180 my_error(ER_TABLE_EXISTS_ERROR, MYF(0), ctx->tmp_name);
7181 return true;
7182 1 default:
7183
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 my_error_innodb(error, table_name, user_table->flags);
7184 1 return true;
7185 }
7186 35697 }
7187
7188 /** Apply the changes made during commit_try_rebuild(),
7189 to the data dictionary cache and the file system.
7190 @param ctx In-place ALTER TABLE context */
7191 35670 inline void commit_cache_rebuild(ha_innobase_inplace_ctx *ctx) {
7192 dberr_t error;
7193
7194
1/2
✓ Branch 0 taken 35670 times.
✗ Branch 1 not taken.
35670 DBUG_TRACE;
7195
3/4
✓ Branch 0 taken 34831 times.
✓ Branch 1 taken 839 times.
✓ Branch 2 taken 34831 times.
✗ Branch 3 not taken.
35670 DEBUG_SYNC_C("commit_cache_rebuild");
7196
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 35670 times.
35670 assert(ctx->need_rebuild());
7197
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 35670 times.
35670 assert(dict_table_is_discarded(ctx->old_table) ==
7198 dict_table_is_discarded(ctx->new_table));
7199
7200 const char *old_name =
7201
1/2
✓ Branch 0 taken 35670 times.
✗ Branch 1 not taken.
35670 mem_heap_strdup(ctx->heap, ctx->old_table->name.m_name);
7202
7203 /* We already committed and redo logged the renames,
7204 so this must succeed. */
7205
1/2
✓ Branch 0 taken 35655 times.
✗ Branch 1 not taken.
35670 error = dict_table_rename_in_cache(ctx->old_table, ctx->tmp_name, false);
7206
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 35655 times.
35655 ut_a(error == DB_SUCCESS);
7207
7208
3/4
✓ Branch 0 taken 34816 times.
✓ Branch 1 taken 839 times.
✓ Branch 2 taken 34816 times.
✗ Branch 3 not taken.
35655 DEBUG_SYNC_C("commit_cache_rebuild_middle");
7209
7210
1/2
✓ Branch 0 taken 35640 times.
✗ Branch 1 not taken.
35655 error = dict_table_rename_in_cache(ctx->new_table, old_name, false);
7211
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 35640 times.
35640 ut_a(error == DB_SUCCESS);
7212 35640 }
7213
7214 /** Set of column numbers */
7215 typedef std::set<ulint, std::less<ulint>, ut::allocator<ulint>> col_set;
7216
7217 /** Store the column number of the columns in a list belonging
7218 to indexes which are not being dropped.
7219 @param[in] ctx In-place ALTER TABLE context
7220 @param[in, out] drop_col_list list which will be set, containing columns
7221 which is part of index being dropped
7222 @param[in, out] drop_v_col_list list which will be set, containing
7223 virtual columns which is part of index
7224 being dropped */
7225 23554 static void get_col_list_to_be_dropped(const ha_innobase_inplace_ctx *ctx,
7226 col_set &drop_col_list,
7227 col_set &drop_v_col_list) {
7228
2/2
✓ Branch 0 taken 3680 times.
✓ Branch 1 taken 23554 times.
27234 for (ulint index_count = 0; index_count < ctx->num_to_drop_index;
7229 index_count++) {
7230 3680 const dict_index_t *index = ctx->drop_index[index_count];
7231
7232
2/2
✓ Branch 0 taken 4201 times.
✓ Branch 1 taken 3680 times.
7881 for (ulint col = 0; col < index->n_user_defined_cols; col++) {
7233 4201 const dict_col_t *idx_col = index->get_col(col);
7234
7235
2/2
✓ Branch 0 taken 126 times.
✓ Branch 1 taken 4075 times.
4201 if (idx_col->is_virtual()) {
7236 126 const dict_v_col_t *v_col =
7237 reinterpret_cast<const dict_v_col_t *>(idx_col);
7238 126 drop_v_col_list.insert(v_col->v_pos);
7239
7240 } else {
7241
1/2
✓ Branch 0 taken 4075 times.
✗ Branch 1 not taken.
4075 ulint col_no = dict_col_get_no(idx_col);
7242
1/2
✓ Branch 0 taken 4075 times.
✗ Branch 1 not taken.
4075 drop_col_list.insert(col_no);
7243 }
7244 }
7245 }
7246 23554 }
7247
7248 /** Commit the changes made during prepare_inplace_alter_table() and
7249 inplace_alter_table() inside the data dictionary tables, when not rebuilding
7250 the table.
7251 @param[in] ha_alter_info Data used during in-place alter
7252 @param[in] ctx In-place ALTER TABLE context
7253 @param[in] trx Data dictionary transaction
7254 @param[in] table_name Table name in MySQL
7255 @retval true Failure
7256 @retval false Success */
7257 23567 [[nodiscard]] inline bool commit_try_norebuild(
7258 Alter_inplace_info *ha_alter_info, ha_innobase_inplace_ctx *ctx, trx_t *trx,
7259 const char *table_name) {
7260
1/2
✓ Branch 0 taken 23567 times.
✗ Branch 1 not taken.
23567 DBUG_TRACE;
7261
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23567 times.
23567 assert(!ctx->need_rebuild());
7262
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23567 times.
23567 assert(trx->dict_operation_lock_mode == RW_X_LATCH);
7263
3/4
✓ Branch 0 taken 56 times.
✓ Branch 1 taken 23511 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 56 times.
23567 assert(
7264 !(ha_alter_info->handler_flags & Alter_inplace_info::DROP_FOREIGN_KEY) ||
7265 ctx->num_to_drop_fk > 0);
7266
7267
2/2
✓ Branch 0 taken 7842 times.
✓ Branch 1 taken 23566 times.
31408 for (ulint i = 0; i < ctx->num_to_add_index; i++) {
7268 7842 dict_index_t *index = ctx->add_index[i];
7269
2/4
✓ Branch 0 taken 7842 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 7842 times.
7842 assert(dict_index_get_online_status(index) == ONLINE_INDEX_COMPLETE);
7270
2/4
✓ Branch 0 taken 7842 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 7842 times.
7842 assert(!index->is_committed());
7271
3/4
✓ Branch 0 taken 7842 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 7841 times.
7842 if (index->is_corrupted()) {
7272 /* Report a duplicate key
7273 error for the index that was
7274 flagged corrupted, most likely
7275 because a duplicate value was
7276 inserted (directly or by
7277 rollback) after
7278 ha_innobase::inplace_alter_table()
7279 completed.
7280 TODO: report this as a corruption
7281 with a detailed reason once
7282 WL#6379 has been implemented. */
7283
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 my_error(ER_DUP_UNKNOWN_IN_INDEX, MYF(0), index->name());
7284 1 return true;
7285 }
7286 }
7287
7288
3/4
✓ Branch 0 taken 23566 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 23564 times.
23566 if (innobase_update_foreign_try(ctx, trx, table_name)) {
7289 2 return true;
7290 }
7291
7292
4/6
✓ Branch 0 taken 23564 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 23562 times.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
23564 DBUG_EXECUTE_IF("ib_rename_column_error",
7293 my_error_innodb(DB_OUT_OF_FILE_SPACE, table_name, 0);
7294 trx->error_state = DB_SUCCESS; trx->op_info = "";
7295 return true;);
7296
7297
4/6
✓ Branch 0 taken 23562 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 23561 times.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
23562 DBUG_EXECUTE_IF("ib_resize_column_error",
7298 my_error_innodb(DB_OUT_OF_FILE_SPACE, table_name, 0);
7299 trx->error_state = DB_SUCCESS; trx->op_info = "";
7300 return true;);
7301
7302
4/6
✓ Branch 0 taken 23561 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 23560 times.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
23561 DBUG_EXECUTE_IF(
7303 "ib_rename_index_fail1", my_error_innodb(DB_DEADLOCK, table_name, 0);
7304 trx->error_state = DB_SUCCESS; trx->op_info = ""; return true;);
7305
7306 23560 return false;
7307 23567 }
7308
7309 /** Commit the changes to the data dictionary cache
7310 after a successful commit_try_norebuild() call.
7311 @param ctx In-place ALTER TABLE context
7312 @param trx Data dictionary transaction object
7313 (will be started and committed)
7314 @return whether all replacements were found for dropped indexes */
7315 23554 [[nodiscard]] inline bool commit_cache_norebuild(ha_innobase_inplace_ctx *ctx,
7316 trx_t *trx) {
7317
1/2
✓ Branch 0 taken 23554 times.
✗ Branch 1 not taken.
23554 DBUG_TRACE;
7318
7319 23554 bool found = true;
7320
7321
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23554 times.
23554 assert(!ctx->need_rebuild());
7322
7323
1/2
✓ Branch 0 taken 23554 times.
✗ Branch 1 not taken.
23554 col_set drop_list;
7324
1/2
✓ Branch 0 taken 23554 times.
✗ Branch 1 not taken.
23554 col_set v_drop_list;
7325 23554 col_set::const_iterator col_it;
7326
7327 /* Check if the column, part of an index to be dropped is part of any
7328 other index which is not being dropped. If not, then set the ord_part
7329 of the column to 0. Here the columns are collected first. */
7330
1/2
✓ Branch 0 taken 23554 times.
✗ Branch 1 not taken.
23554 get_col_list_to_be_dropped(ctx, drop_list, v_drop_list);
7331
7332
2/2
✓ Branch 0 taken 7834 times.
✓ Branch 1 taken 23554 times.
31388 for (ulint i = 0; i < ctx->num_to_add_index; i++) {
7333 7834 dict_index_t *index = ctx->add_index[i];
7334
2/4
✓ Branch 0 taken 7834 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 7834 times.
7834 assert(dict_index_get_online_status(index) == ONLINE_INDEX_COMPLETE);
7335
2/4
✓ Branch 0 taken 7834 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 7834 times.
7834 assert(!index->is_committed());
7336
1/2
✓ Branch 0 taken 7834 times.
✗ Branch 1 not taken.
7834 index->set_committed(true);
7337 }
7338
7339
2/2
✓ Branch 0 taken 3487 times.
✓ Branch 1 taken 20067 times.
23554 if (ctx->num_to_drop_index) {
7340 /* Drop indexes in data dictionary cache and write
7341 DDL log for them */
7342
2/2
✓ Branch 0 taken 3680 times.
✓ Branch 1 taken 3487 times.
7167 for (ulint i = 0; i < ctx->num_to_drop_index; i++) {
7343 3680 dict_index_t *index = ctx->drop_index[i];
7344
2/4
✓ Branch 0 taken 3680 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 3680 times.
3680 assert(index->is_committed());
7345
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3680 times.
3680 assert(index->table == ctx->new_table);
7346
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3680 times.
3680 assert(index->to_be_dropped);
7347
7348 /* Replace the indexes in foreign key
7349 constraints if needed. */
7350
2/4
✓ Branch 0 taken 3680 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 3680 times.
3680 if (!dict_foreign_replace_index(index->table, ctx->col_names, index)) {
7351 found = false;
7352 }
7353 }
7354
7355
2/2
✓ Branch 0 taken 3678 times.
✓ Branch 1 taken 3477 times.
7155 for (ulint i = 0; i < ctx->num_to_drop_index; i++) {
7356 3678 dict_index_t *index = ctx->drop_index[i];
7357
2/4
✓ Branch 0 taken 3678 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 3678 times.
3678 assert(index->is_committed());
7358
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3678 times.
3678 assert(index->table == ctx->new_table);
7359
7360
2/2
✓ Branch 0 taken 98 times.
✓ Branch 1 taken 3580 times.
3678 if (index->type & DICT_FTS) {
7361
1/6
✗ Branch 0 not taken.
✓ Branch 1 taken 98 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
98 assert(index->type == DICT_FTS || index->is_corrupted());
7362
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 98 times.
98 assert(index->table->fts);
7363
1/2
✓ Branch 0 taken 98 times.
✗ Branch 1 not taken.
98 ctx->fts_drop_aux_vec = new aux_name_vec_t;
7364
1/2
✓ Branch 0 taken 98 times.
✗ Branch 1 not taken.
98 fts_drop_index(index->table, index, trx, ctx->fts_drop_aux_vec);
7365 }
7366
7367 /* It is a single table tablespace and the .ibd file is
7368 missing if root is FIL_NULL, do nothing. */
7369
2/2
✓ Branch 0 taken 3577 times.
✓ Branch 1 taken 101 times.
3678 if (index->page != FIL_NULL) {
7370
1/2
✓ Branch 0 taken 3577 times.
✗ Branch 1 not taken.
3577 dict_sys_mutex_exit();
7371
1/2
✓ Branch 0 taken 3567 times.
✗ Branch 1 not taken.
3577 ut_d(dberr_t err =) log_ddl->write_free_tree_log(trx, index, true);
7372
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3567 times.
3567 ut_ad(err == DB_SUCCESS);
7373
1/2
✓ Branch 0 taken 3567 times.
✗ Branch 1 not taken.
3567 dict_sys_mutex_enter();
7374 }
7375
7376
1/2
✓ Branch 0 taken 3668 times.
✗ Branch 1 not taken.
3668 btr_drop_ahi_for_index(index);
7377
1/2
✓ Branch 0 taken 3668 times.
✗ Branch 1 not taken.
3668 dict_index_remove_from_cache(index->table, index);
7378 }
7379 }
7380
7381 /* Update the ord_part after index dropped, to get accurate values */
7382
2/2
✓ Branch 0 taken 3803 times.
✓ Branch 1 taken 23544 times.
27347 for (col_it = drop_list.begin(); col_it != drop_list.end(); ++col_it) {
7383
3/4
✓ Branch 0 taken 3803 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3539 times.
✓ Branch 3 taken 264 times.
3803 if (!check_col_exists_in_indexes(ctx->new_table, *col_it, false)) {
7384 3539 ctx->new_table->cols[*col_it].ord_part = 0;
7385 }
7386 }
7387
7388
2/2
✓ Branch 0 taken 121 times.
✓ Branch 1 taken 23544 times.
23665 for (col_it = v_drop_list.begin(); col_it != v_drop_list.end(); ++col_it) {
7389
3/4
✓ Branch 0 taken 121 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 110 times.
✓ Branch 3 taken 11 times.
121 if (!check_col_exists_in_indexes(ctx->new_table, *col_it, true)) {
7390 110 ctx->new_table->v_cols[*col_it].m_col.ord_part = 0;
7391 }
7392 }
7393
7394 23544 ctx->new_table->fts_doc_id_index =
7395 23544 ctx->new_table->fts
7396
3/4
✓ Branch 0 taken 419 times.
✓ Branch 1 taken 23125 times.
✓ Branch 2 taken 419 times.
✗ Branch 3 not taken.
23544 ? dict_table_get_index_on_name(ctx->new_table, FTS_DOC_ID_INDEX_NAME)
7397 : nullptr;
7398
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23544 times.
23544 assert((ctx->new_table->fts == nullptr) ==
7399 (ctx->new_table->fts_doc_id_index == nullptr));
7400
7401 23544 return found;
7402 23544 }
7403
7404 /** Adjust the persistent statistics after non-rebuilding ALTER TABLE.
7405 Remove statistics for dropped indexes, add statistics for created indexes
7406 and rename statistics for renamed indexes.
7407 @param ha_alter_info Data used during in-place alter
7408 @param ctx In-place ALTER TABLE context
7409 @param table_name Table name in MySQL
7410 @param thd MySQL connection
7411 */
7412 23183 static void alter_stats_norebuild(Alter_inplace_info *ha_alter_info,
7413 ha_innobase_inplace_ctx *ctx,
7414 const char *table_name, THD *thd) {
7415 ulint i;
7416
7417
1/2
✓ Branch 0 taken 23183 times.
✗ Branch 1 not taken.
23183 DBUG_TRACE;
7418
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23183 times.
23183 assert(!ctx->need_rebuild());
7419
7420
3/4
✓ Branch 0 taken 23183 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 6168 times.
✓ Branch 3 taken 17015 times.
23183 if (!dict_stats_is_persistent_enabled(ctx->new_table)) {
7421 6168 return;
7422 }
7423
7424 /* Delete corresponding rows from the stats table. We do this
7425 in a separate transaction from trx, because lock waits are not
7426 allowed in a data dictionary transaction. (Lock waits are possible
7427 on the statistics table, because it is directly accessible by users,
7428 not covered by the dict_operation_lock.)
7429
7430 Because the data dictionary changes were already committed, orphaned
7431 rows may be left in the statistics table if the system crashes.
7432
7433 FIXME: each change to the statistics tables is being committed in a
7434 separate transaction, meaning that the operation is not atomic
7435
7436 FIXME: This will not drop the (unused) statistics for
7437 FTS_DOC_ID_INDEX if it was a hidden index, dropped together
7438 with the last renamining FULLTEXT index. */
7439
2/2
✓ Branch 0 taken 3533 times.
✓ Branch 1 taken 17015 times.
20548 for (i = 0; i < ha_alter_info->index_drop_count; i++) {
7440 3533 const KEY *key = ha_alter_info->index_drop_buffer[i];
7441
7442
2/2
✓ Branch 0 taken 96 times.
✓ Branch 1 taken 3437 times.
3533 if (key->flags & HA_FULLTEXT) {
7443 /* There are no index cardinality
7444 statistics for FULLTEXT indexes. */
7445 96 continue;
7446 }
7447
7448 char errstr[ERROR_STR_LENGTH];
7449
7450
1/2
✓ Branch 0 taken 3437 times.
✗ Branch 1 not taken.
3437 if (dict_stats_drop_index(ctx->new_table->name.m_name, key->name, errstr,
7451
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 3436 times.
3437 sizeof errstr) != DB_SUCCESS) {
7452
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 push_warning(thd, Sql_condition::SL_WARNING, ER_LOCK_WAIT_TIMEOUT,
7453 errstr);
7454 }
7455 }
7456
7457
2/2
✓ Branch 0 taken 109 times.
✓ Branch 1 taken 17015 times.
17124 for (i = 0; i < ha_alter_info->index_rename_count; i++) {
7458 109 KEY_PAIR *pair = &ha_alter_info->index_rename_buffer[i];
7459 dberr_t err;
7460
7461 218 err = dict_stats_rename_index(ctx->new_table, pair->old_key->name,
7462
1/2
✓ Branch 0 taken 109 times.
✗ Branch 1 not taken.
109 pair->new_key->name);
7463
7464
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 109 times.
109 if (err != DB_SUCCESS) {
7465 push_warning_printf(thd, Sql_condition::SL_WARNING, ER_ERROR_ON_RENAME,
7466 "Error renaming an index of table '%s'"
7467 " from '%s' to '%s' in InnoDB persistent"
7468 " statistics storage: %s",
7469 table_name, pair->old_key->name, pair->new_key->name,
7470 ut_strerr(err));
7471 }
7472 }
7473
7474
2/2
✓ Branch 0 taken 7607 times.
✓ Branch 1 taken 17015 times.
24622 for (i = 0; i < ctx->num_to_add_index; i++) {
7475 7607 dict_index_t *index = ctx->add_index[i];
7476
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 7607 times.
7607 assert(index->table == ctx->new_table);
7477
7478
2/2
✓ Branch 0 taken 7314 times.
✓ Branch 1 taken 293 times.
7607 if (!(index->type & DICT_FTS)) {
7479
1/2
✓ Branch 0 taken 7314 times.
✗ Branch 1 not taken.
7314 dict_stats_init(ctx->new_table);
7480
1/2
✓ Branch 0 taken 7314 times.
✗ Branch 1 not taken.
7314 dict_stats_update_for_index(index);
7481 }
7482 }
7483
2/2
✓ Branch 0 taken 17015 times.
✓ Branch 1 taken 6168 times.
23183 }
7484
7485 /** Adjust the persistent statistics after rebuilding ALTER TABLE.
7486 Remove statistics for dropped indexes, add statistics for created indexes
7487 and rename statistics for renamed indexes.
7488 @param table InnoDB table that was rebuilt by ALTER TABLE
7489 @param table_name Table name in MySQL
7490 @param thd MySQL connection
7491 */
7492 35587 static void alter_stats_rebuild(dict_table_t *table, const char *table_name,
7493 THD *thd) {
7494
1/2
✓ Branch 0 taken 35587 times.
✗ Branch 1 not taken.
35587 DBUG_TRACE;
7495
2/6
✓ Branch 0 taken 35587 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 35587 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
35587 DBUG_EXECUTE_IF("ib_ddl_crash_before_rename", DBUG_SUICIDE(););
7496
7497
4/4
✓ Branch 0 taken 35583 times.
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 17476 times.
✓ Branch 3 taken 18111 times.
71170 if (dict_table_is_discarded(table) ||
7498
3/4
✓ Branch 0 taken 35583 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 17472 times.
✓ Branch 3 taken 18111 times.
35583 !dict_stats_is_persistent_enabled(table)) {
7499 17476 return;
7500 }
7501
7502 #ifdef UNIV_DEBUG
7503 18111 bool ibd_file_missing_orig = false;
7504 #endif /* UNIV_DEBUG */
7505
7506
3/4
✓ Branch 0 taken 18111 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 18110 times.
18111 DBUG_EXECUTE_IF("ib_rename_index_fail2",
7507 ibd_file_missing_orig = table->ibd_file_missing;
7508 table->ibd_file_missing = true;);
7509
7510
1/2
✓ Branch 0 taken 18111 times.
✗ Branch 1 not taken.
18111 dberr_t ret = dict_stats_update(table, DICT_STATS_RECALC_PERSISTENT);
7511
7512
3/4
✓ Branch 0 taken 18111 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 18110 times.
18111 DBUG_EXECUTE_IF("ib_rename_index_fail2",
7513 table->ibd_file_missing = ibd_file_missing_orig;);
7514
7515
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 18110 times.
18111 if (ret != DB_SUCCESS) {
7516
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 push_warning_printf(thd, Sql_condition::SL_WARNING, ER_ALTER_INFO,
7517 "Error updating stats for table '%s'"
7518 " after table rebuild: %s",
7519 table_name, ut_strerr(ret));
7520 }
7521
2/2
✓ Branch 0 taken 18111 times.
✓ Branch 1 taken 17476 times.
35587 }
7522
7523 /** Implementation of commit_inplace_alter_table()
7524 @tparam Table dd::Table or dd::Partition
7525 @param[in] altered_table TABLE object for new version of table.
7526 @param[in,out] ha_alter_info Structure describing changes to be done
7527 by ALTER TABLE and holding data used
7528 during in-place alter.
7529 @param[in] commit True to commit or false to rollback.
7530 @param[in,out] new_dd_tab Table object for the new version of the
7531 table. Can be adjusted by this call.
7532 Changes to the table definition
7533 will be persisted in the data-dictionary
7534 at statement version of it.
7535 @retval true Failure
7536 @retval false Success */
7537 template <typename Table>
7538 64315 bool ha_innobase::commit_inplace_alter_table_impl(
7539 TABLE *altered_table, Alter_inplace_info *ha_alter_info, bool commit,
7540 Table *new_dd_tab) {
7541 dberr_t error;
7542 ha_innobase_inplace_ctx *ctx0;
7543
1/2
✓ Branch 0 taken 64315 times.
✗ Branch 1 not taken.
64315 struct mtr_buf_copy_t logs;
7544
7545 64315 ctx0 = static_cast<ha_innobase_inplace_ctx *>(ha_alter_info->handler_ctx);
7546
7547 #ifdef UNIV_DEBUG
7548 64315 uint crash_inject_count = 1;
7549 64315 uint crash_fail_inject_count = 1;
7550 64315 uint failure_inject_count = 1;
7551 #endif /* UNIV_DEBUG */
7552
7553
1/2
✓ Branch 0 taken 64315 times.
✗ Branch 1 not taken.
64315 DBUG_TRACE;
7554
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 64315 times.
64315 assert(!srv_read_only_mode);
7555
3/4
✓ Branch 0 taken 54060 times.
✓ Branch 1 taken 10255 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 54060 times.
64315 assert(!ctx0 || ctx0->prebuilt == m_prebuilt);
7556
3/4
✓ Branch 0 taken 54060 times.
✓ Branch 1 taken 10255 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 54060 times.
64315 assert(!ctx0 || ctx0->old_table == m_prebuilt->table);
7557
7558
3/4
✓ Branch 0 taken 62738 times.
✓ Branch 1 taken 1577 times.
✓ Branch 2 taken 62738 times.
✗ Branch 3 not taken.
64315 DEBUG_SYNC_C("innodb_commit_inplace_alter_table_enter");
7559
7560
3/4
✓ Branch 0 taken 62738 times.
✓ Branch 1 taken 1577 times.
✓ Branch 2 taken 62738 times.
✗ Branch 3 not taken.
64315 DEBUG_SYNC_C("innodb_commit_inplace_alter_table_wait");
7561
7562
4/4
✓ Branch 0 taken 54060 times.
✓ Branch 1 taken 10255 times.
✓ Branch 2 taken 38480 times.
✓ Branch 3 taken 15580 times.
64315 if (ctx0 != nullptr && ctx0->m_stage != nullptr) {
7563
1/2
✓ Branch 0 taken 38480 times.
✗ Branch 1 not taken.
38480 ctx0->m_stage->begin_phase_end();
7564 }
7565
7566
2/2
✓ Branch 0 taken 737 times.
✓ Branch 1 taken 63578 times.
64315 if (!commit) {
7567 /* A rollback is being requested. So far we may at
7568 most have created some indexes. If any indexes were to
7569 be dropped, they would actually be dropped in this
7570 method if commit=true. */
7571 const bool ret =
7572
1/2
✓ Branch 0 taken 737 times.
✗ Branch 1 not taken.
737 rollback_inplace_alter_table(ha_alter_info, table, m_prebuilt);
7573 737 return ret;
7574 }
7575
7576
6/6
✓ Branch 0 taken 54921 times.
✓ Branch 1 taken 8657 times.
✓ Branch 2 taken 1417 times.
✓ Branch 3 taken 53504 times.
✓ Branch 4 taken 10074 times.
✓ Branch 5 taken 53504 times.
118499 if (!(ha_alter_info->handler_flags & ~INNOBASE_INPLACE_IGNORE) ||
7577 54921 is_instant(ha_alter_info)) {
7578
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 10074 times.
10074 assert(!ctx0);
7579 10074 MONITOR_ATOMIC_DEC(MONITOR_PENDING_ALTER_TABLE);
7580 10074 ha_alter_info->group_commit_ctx = nullptr;
7581 10074 return false;
7582 }
7583
7584
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 53504 times.
53504 assert(ctx0);
7585
7586 inplace_alter_handler_ctx **ctx_array;
7587 inplace_alter_handler_ctx *ctx_single[2];
7588
7589
2/2
✓ Branch 0 taken 1244 times.
✓ Branch 1 taken 52260 times.
53504 if (ha_alter_info->group_commit_ctx) {
7590 1244 ctx_array = ha_alter_info->group_commit_ctx;
7591 } else {
7592 52260 ctx_single[0] = ctx0;
7593 52260 ctx_single[1] = nullptr;
7594 52260 ctx_array = ctx_single;
7595 }
7596
7597
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 53504 times.
53504 assert(ctx0 == ctx_array[0]);
7598
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 53504 times.
53504 ut_ad(m_prebuilt->table == ctx0->old_table);
7599 53504 ha_alter_info->group_commit_ctx = nullptr;
7600
7601
1/2
✓ Branch 0 taken 53504 times.
✗ Branch 1 not taken.
53504 trx_start_if_not_started_xa(m_prebuilt->trx, true, UT_LOCATION_HERE);
7602
7603
2/2
✓ Branch 0 taken 59264 times.
✓ Branch 1 taken 53504 times.
112768 for (inplace_alter_handler_ctx **pctx = ctx_array; *pctx; pctx++) {
7604 59264 ha_innobase_inplace_ctx *ctx =
7605 static_cast<ha_innobase_inplace_ctx *>(*pctx);
7606
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 59264 times.
59264 assert(ctx->prebuilt->trx == m_prebuilt->trx);
7607
7608 /* Exclusively lock the table, to ensure that no other
7609 transaction is holding locks on the table while we
7610 change the table definition. The MySQL meta-data lock
7611 should normally guarantee that no conflicting locks
7612 exist. However, FOREIGN KEY constraints checks and any
7613 transactions collected during crash recovery could be
7614 holding InnoDB locks only, not MySQL locks. */
7615
7616 59264 error = ddl::lock_table(m_prebuilt->trx, ctx->old_table, LOCK_X);
7617
7618
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 59264 times.
59264 if (error != DB_SUCCESS) {
7619 my_error_innodb(error, table_share->table_name.str, 0);
7620 return true;
7621 }
7622 }
7623
7624
3/4
✓ Branch 0 taken 52230 times.
✓ Branch 1 taken 1274 times.
✓ Branch 2 taken 52230 times.
✗ Branch 3 not taken.
53504 DEBUG_SYNC(m_user_thd, "innodb_alter_commit_after_lock_table");
7625
7626 53504 const bool new_clustered = ctx0->need_rebuild();
7627 53504 trx_t *trx = ctx0->trx;
7628 53504 bool fail = false;
7629
7630
2/2
✓ Branch 0 taken 30711 times.
✓ Branch 1 taken 22793 times.
53504 if (new_clustered) {
7631
2/2
✓ Branch 0 taken 35697 times.
✓ Branch 1 taken 30711 times.
66408 for (inplace_alter_handler_ctx **pctx = ctx_array; *pctx; pctx++) {
7632 35697 ha_innobase_inplace_ctx *ctx =
7633 static_cast<ha_innobase_inplace_ctx *>(*pctx);
7634
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 35697 times.
35697 assert(ctx->need_rebuild());
7635
7636
2/2
✓ Branch 0 taken 20 times.
✓ Branch 1 taken 35677 times.
35697 if (ctx->old_table->fts) {
7637
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 20 times.
20 ut_ad(!ctx->old_table->fts->add_wq);
7638
1/2
✓ Branch 0 taken 20 times.
✗ Branch 1 not taken.
20 fts_optimize_remove_table(ctx->old_table);
7639 }
7640
7641
2/2
✓ Branch 0 taken 120 times.
✓ Branch 1 taken 35577 times.
35697 if (ctx->new_table->fts) {
7642
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 120 times.
120 ut_ad(!ctx->new_table->fts->add_wq);
7643
1/2
✓ Branch 0 taken 120 times.
✗ Branch 1 not taken.
120 fts_optimize_remove_table(ctx->new_table);
7644 }
7645 }
7646 }
7647
7648
2/2
✓ Branch 0 taken 15570 times.
✓ Branch 1 taken 37934 times.
53504 if (trx == nullptr) {
7649 15570 trx = m_prebuilt->trx;
7650 15570 ctx0->trx = trx;
7651
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 15570 times.
15570 assert(!new_clustered);
7652 }
7653
7654 /* Generate the temporary name for old table, and acquire mdl
7655 lock on it. */
7656
1/2
✓ Branch 0 taken 53504 times.
✗ Branch 1 not taken.
53504 THD *thd = current_thd;
7657
2/2
✓ Branch 0 taken 59264 times.
✓ Branch 1 taken 53504 times.
112768 for (inplace_alter_handler_ctx **pctx = ctx_array; *pctx; pctx++) {
7658 59264 ha_innobase_inplace_ctx *ctx =
7659 static_cast<ha_innobase_inplace_ctx *>(*pctx);
7660
7661
2/2
✓ Branch 0 taken 35697 times.
✓ Branch 1 taken 23567 times.
59264 if (ctx->need_rebuild()) {
7662 71394 ctx->tmp_name = dict_mem_create_temporary_tablename(
7663
1/2
✓ Branch 0 taken 35697 times.
✗ Branch 1 not taken.
35697 ctx->heap, ctx->new_table->name.m_name, ctx->new_table->id);
7664
7665 35697 std::string db_str;
7666 35697 std::string tbl_str;
7667
2/4
✓ Branch 0 taken 35697 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 35697 times.
✗ Branch 3 not taken.
35697 dict_name::get_table(ctx->tmp_name, db_str, tbl_str);
7668
7669 /* Acquire mdl lock on the temporary table name. */
7670 35697 MDL_ticket *mdl_ticket = nullptr;
7671
2/4
✓ Branch 0 taken 35697 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 35697 times.
35697 if (dd::acquire_exclusive_table_mdl(thd, db_str.c_str(), tbl_str.c_str(),
7672 false, &mdl_ticket)) {
7673 return true;
7674 }
7675
2/4
✓ Branch 0 taken 35697 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 35697 times.
✗ Branch 3 not taken.
35697 }
7676 }
7677
7678 /* Latch the InnoDB data dictionary exclusively so that no deadlocks
7679 or lock waits can happen in it during the data dictionary operation. */
7680
1/2
✓ Branch 0 taken 53504 times.
✗ Branch 1 not taken.
53504 row_mysql_lock_data_dictionary(trx, UT_LOCATION_HERE);
7681
7682 /* Prevent the background statistics collection from accessing
7683 the tables. */
7684 for (;;) {
7685 53504 bool retry = false;
7686
7687
2/2
✓ Branch 0 taken 59264 times.
✓ Branch 1 taken 53504 times.
112768 for (inplace_alter_handler_ctx **pctx = ctx_array; *pctx; pctx++) {
7688 59264 ha_innobase_inplace_ctx *ctx =
7689 static_cast<ha_innobase_inplace_ctx *>(*pctx);
7690
7691
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 59264 times.
59264 assert(new_clustered == ctx->need_rebuild());
7692
7693
5/8
✓ Branch 0 taken 35697 times.
✓ Branch 1 taken 23567 times.
✓ Branch 2 taken 35697 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 35697 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 59264 times.
59264 if (new_clustered && !dict_stats_stop_bg(ctx->old_table)) {
7694 retry = true;
7695 }
7696
7697
2/4
✓ Branch 0 taken 59264 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 59264 times.
59264 if (!dict_stats_stop_bg(ctx->new_table)) {
7698 retry = true;
7699 }
7700 }
7701
7702
1/2
✓ Branch 0 taken 53504 times.
✗ Branch 1 not taken.
53504 if (!retry) {
7703 53504 break;
7704 }
7705
7706 DICT_STATS_BG_YIELD(trx, UT_LOCATION_HERE);
7707 }
7708
7709 /* Apply the changes to the data dictionary tables, for all partitions.*/
7710
7711
3/4
✓ Branch 0 taken 53503 times.
✓ Branch 1 taken 59264 times.
✓ Branch 2 taken 59264 times.
✗ Branch 3 not taken.
112767 for (inplace_alter_handler_ctx **pctx = ctx_array; *pctx && !fail; pctx++) {
7712 59264 ha_innobase_inplace_ctx *ctx =
7713 static_cast<ha_innobase_inplace_ctx *>(*pctx);
7714
7715
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 59264 times.
59264 assert(new_clustered == ctx->need_rebuild());
7716
7717
1/2
✓ Branch 0 taken 59264 times.
✗ Branch 1 not taken.
59264 fail = commit_get_autoinc(ha_alter_info, ctx, altered_table, table);
7718
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 59264 times.
59264 if (fail) {
7719 my_error(ER_TABLESPACE_DISCARDED, MYF(0), table->s->table_name.str);
7720 goto rollback_trx;
7721 }
7722
7723
2/2
✓ Branch 0 taken 35697 times.
✓ Branch 1 taken 23567 times.
59264 if (ctx->need_rebuild()) {
7724 71394 fail = commit_try_rebuild(ha_alter_info, ctx, altered_table, table, trx,
7725
1/2
✓ Branch 0 taken 35697 times.
✗ Branch 1 not taken.
35697 table_share->table_name.str);
7726
7727
2/2
✓ Branch 0 taken 35695 times.
✓ Branch 1 taken 2 times.
35697 if (!fail) {
7728
1/2
✓ Branch 0 taken 35694 times.
✗ Branch 1 not taken.
35695 log_ddl->write_drop_log(trx, ctx->old_table->id);
7729 }
7730 } else {
7731 23567 fail = commit_try_norebuild(ha_alter_info, ctx, trx,
7732
1/2
✓ Branch 0 taken 23567 times.
✗ Branch 1 not taken.
23567 table_share->table_name.str);
7733 }
7734
2/6
✓ Branch 0 taken 59263 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 59263 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
59263 DBUG_INJECT_CRASH("ib_commit_inplace_crash", crash_inject_count++);
7735 #ifdef UNIV_DEBUG
7736 {
7737 /* Generate a dynamic dbug text. */
7738 char buf[32];
7739
7740 59263 snprintf(buf, sizeof buf, "ib_commit_inplace_fail_%u",
7741 failure_inject_count++);
7742
7743
2/6
✓ Branch 0 taken 59263 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 59263 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
59263 DBUG_EXECUTE_IF(buf,
7744 my_error(ER_INTERNAL_ERROR, MYF(0), "Injected error!");
7745 fail = true;);
7746 }
7747 #endif
7748 }
7749
7750 53503 rollback_trx:
7751
7752 /* Commit or roll back the changes to the data dictionary. */
7753
7754
4/4
✓ Branch 0 taken 53494 times.
✓ Branch 1 taken 9 times.
✓ Branch 2 taken 30708 times.
✓ Branch 3 taken 22786 times.
53503 if (!fail && new_clustered) {
7755
2/2
✓ Branch 0 taken 35694 times.
✓ Branch 1 taken 30708 times.
66402 for (inplace_alter_handler_ctx **pctx = ctx_array; *pctx; pctx++) {
7756 35694 ha_innobase_inplace_ctx *ctx =
7757 static_cast<ha_innobase_inplace_ctx *>(*pctx);
7758
7759
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 35694 times.
35694 assert(ctx->need_rebuild());
7760
7761 /* Check for any possible problems for any
7762 file operations that will be performed in
7763 commit_cache_rebuild(). */
7764 error =
7765
1/2
✓ Branch 0 taken 35694 times.
✗ Branch 1 not taken.
35694 fil_rename_precheck(ctx->old_table, ctx->new_table, ctx->tmp_name);
7766
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 35691 times.
35694 if (error != DB_SUCCESS) {
7767 /* Out of memory or a problem will occur
7768 when renaming files. */
7769 3 fail = true;
7770 3 my_error_innodb(error, ctx->old_table->name.m_name,
7771
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 ctx->old_table->flags);
7772 }
7773
2/6
✓ Branch 0 taken 35694 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 35694 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
35694 DBUG_INJECT_CRASH("ib_commit_inplace_crash", crash_inject_count++);
7774 }
7775
7776 /* Test what happens on crash here.
7777 The data dictionary transaction should be
7778 rolled back, restoring the old table. */
7779
5/8
✓ Branch 0 taken 30708 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 30707 times.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 1 times.
✗ Branch 7 not taken.
30708 DBUG_EXECUTE_IF("innodb_alter_commit_crash_before_commit",
7780 log_buffer_flush_to_disk();
7781 DBUG_SUICIDE(););
7782
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 30707 times.
30707 ut_ad(!trx->fts_trx);
7783
7784
6/10
✓ Branch 0 taken 30707 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 30705 times.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 2 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 2 times.
✗ Branch 9 not taken.
30707 DBUG_EXECUTE_IF("innodb_alter_commit_crash_after_commit",
7785 log_make_latest_checkpoint();
7786 log_buffer_flush_to_disk(); DBUG_SUICIDE(););
7787 }
7788
7789 /* Update the in-memory structures, close some handles, release
7790 temporary files, and (unless we rolled back) update persistent
7791 statistics. */
7792
2/2
✓ Branch 0 taken 59236 times.
✓ Branch 1 taken 53460 times.
112696 for (inplace_alter_handler_ctx **pctx = ctx_array; *pctx; pctx++) {
7793 59236 ha_innobase_inplace_ctx *ctx =
7794 static_cast<ha_innobase_inplace_ctx *>(*pctx);
7795
7796
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 59236 times.
59236 assert(ctx->need_rebuild() == new_clustered);
7797
7798
2/2
✓ Branch 0 taken 35675 times.
✓ Branch 1 taken 23561 times.
59236 if (new_clustered) {
7799
1/2
✓ Branch 0 taken 35675 times.
✗ Branch 1 not taken.
35675 innobase_online_rebuild_log_free(ctx->old_table);
7800 }
7801
7802
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 59224 times.
59236 if (fail) {
7803
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 7 times.
12 if (new_clustered) {
7804
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
5 dict_table_close(ctx->new_table, true, false);
7805 5 ctx->new_table = nullptr;
7806 } else {
7807 /* We failed, but did not rebuild the table.
7808 Roll back any ADD INDEX, or get rid of garbage
7809 ADD INDEX that was left over from a previous
7810 ALTER TABLE statement. */
7811
1/2
✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
7 innobase_rollback_sec_index(ctx->new_table, table, true, trx);
7812 }
7813
2/6
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 12 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
12 DBUG_INJECT_CRASH("ib_commit_inplace_crash_fail",
7814 crash_fail_inject_count++);
7815
7816 12 continue;
7817 12 }
7818
7819
1/2
✓ Branch 0 taken 59224 times.
✗ Branch 1 not taken.
59224 innobase_copy_frm_flags_from_table_share(ctx->new_table, altered_table->s);
7820
7821
2/2
✓ Branch 0 taken 35670 times.
✓ Branch 1 taken 23554 times.
59224 if (new_clustered) {
7822 /* We will reload and refresh the
7823 in-memory foreign key constraint
7824 metadata. This is a rename operation
7825 in preparing for dropping the old
7826 table. Set the table to_be_dropped bit
7827 here, so to make sure DML foreign key
7828 constraint check does not use the
7829 stale dict_foreign_t. This is done
7830 because WL#6049 (FK MDL) has not been
7831 implemented yet. */
7832 35670 ctx->old_table->to_be_dropped = true;
7833
7834
3/8
✓ Branch 0 taken 35670 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 35670 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 35670 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
35670 DBUG_PRINT("to_be_dropped", ("table: %s", ctx->old_table->name.m_name));
7835
7836
2/2
✓ Branch 0 taken 32 times.
✓ Branch 1 taken 35638 times.
35670 if ((ha_alter_info->handler_flags &
7837 Alter_inplace_info::ALTER_COLUMN_NAME)) {
7838
1/2
✓ Branch 0 taken 32 times.
✗ Branch 1 not taken.
32 innobase_rename_col_discard_foreign(ha_alter_info, table,
7839 ctx->old_table);
7840 }
7841
7842 /* Rename the tablespace files. */
7843
1/2
✓ Branch 0 taken 35640 times.
✗ Branch 1 not taken.
35670 commit_cache_rebuild(ctx);
7844
7845 /* Discard the added foreign keys, because we will
7846 load them from the data dictionary. */
7847
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 35640 times.
35648 for (ulint i = 0; i < ctx->num_to_add_fk; i++) {
7848 8 dict_foreign_t *fk = ctx->add_fk[i];
7849
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 dict_foreign_free(fk);
7850 }
7851
7852 /* There is no FK on partition table */
7853
2/2
✓ Branch 0 taken 29646 times.
✓ Branch 1 taken 5994 times.
35640 if (m_share) {
7854 29646 ctx->new_table->discard_after_ddl = true;
7855 }
7856 } else {
7857 error =
7858
1/2
✓ Branch 0 taken 23554 times.
✗ Branch 1 not taken.
23554 innobase_update_foreign_cache(ctx, m_user_thd, &new_dd_tab->table());
7859
7860
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23554 times.
23554 if (error != DB_SUCCESS) {
7861 /* The data dictionary cache
7862 should be corrupted now. The
7863 best solution should be to
7864 kill and restart the server,
7865 but the *.frm file has not
7866 been replaced yet. */
7867 push_warning_printf(m_user_thd, Sql_condition::SL_WARNING,
7868 ER_ALTER_INFO,
7869 "InnoDB: Could not add foreign"
7870 " key constraints.");
7871 } else {
7872
2/4
✓ Branch 0 taken 23544 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 23544 times.
23554 if (!commit_cache_norebuild(ctx, trx)) {
7873 ut_a(!m_prebuilt->trx->check_foreigns);
7874 }
7875
7876
1/2
✓ Branch 0 taken 23544 times.
✗ Branch 1 not taken.
23544 innobase_rename_or_enlarge_columns_cache(ha_alter_info, table,
7877 ctx->new_table);
7878
7879
1/2
✓ Branch 0 taken 23544 times.
✗ Branch 1 not taken.
23544 rename_indexes_in_cache(ctx, ha_alter_info);
7880 }
7881 }
7882
7883
1/2
✓ Branch 0 taken 59184 times.
✗ Branch 1 not taken.
59184 dict_mem_table_free_foreign_vcol_set(ctx->new_table);
7884
1/2
✓ Branch 0 taken 59184 times.
✗ Branch 1 not taken.
59184 dict_mem_table_fill_foreign_vcol_set(ctx->new_table);
7885
7886
2/6
✓ Branch 0 taken 59184 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 59184 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
59184 DBUG_INJECT_CRASH("ib_commit_inplace_crash", crash_inject_count++);
7887 }
7888
7889 /* Invalidate the index translation table. In partitioned
7890 tables, there is no share. */
7891
2/2
✓ Branch 0 taken 52240 times.
✓ Branch 1 taken 1220 times.
53460 if (m_share) {
7892 52240 m_share->idx_trans_tbl.index_count = 0;
7893 }
7894
7895 /* Tell the InnoDB server that there might be work for
7896 utility threads: */
7897
7898
1/2
✓ Branch 0 taken 53460 times.
✗ Branch 1 not taken.
53460 srv_active_wake_master_thread();
7899
7900
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 53448 times.
53460 if (fail) {
7901
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 12 times.
24 for (inplace_alter_handler_ctx **pctx = ctx_array; *pctx; pctx++) {
7902 12 ha_innobase_inplace_ctx *ctx =
7903 static_cast<ha_innobase_inplace_ctx *>(*pctx);
7904
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
12 assert(ctx->need_rebuild() == new_clustered);
7905
7906
1/2
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
12 ut_d(dict_table_check_for_dup_indexes(ctx->old_table, CHECK_ABORTED_OK));
7907
2/4
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 12 times.
12 ut_a(fts_check_cached_index(ctx->old_table));
7908
2/6
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 12 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
12 DBUG_INJECT_CRASH("ib_commit_inplace_crash_fail",
7909 crash_fail_inject_count++);
7910 }
7911
7912
1/2
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
12 row_mysql_unlock_data_dictionary(trx);
7913 12 return true;
7914 }
7915
7916
2/2
✓ Branch 0 taken 53353 times.
✓ Branch 1 taken 95 times.
53448 if (ha_alter_info->virtual_column_drop_count ||
7917
2/2
✓ Branch 0 taken 256 times.
✓ Branch 1 taken 53097 times.
53353 ha_alter_info->virtual_column_add_count) {
7918
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 351 times.
351 if (ctx0->old_table->get_ref_count() > 1) {
7919 row_mysql_unlock_data_dictionary(trx);
7920 my_error(ER_TABLE_REFERENCED, MYF(0));
7921 return true;
7922 }
7923
7924
2/2
✓ Branch 0 taken 351 times.
✓ Branch 1 taken 351 times.
702 for (inplace_alter_handler_ctx **pctx = ctx_array; *pctx; pctx++) {
7925 351 ha_innobase_inplace_ctx *ctx =
7926 static_cast<ha_innobase_inplace_ctx *>(*pctx);
7927
7928 /* Drop outdated table stats. */
7929
1/2
✓ Branch 0 taken 351 times.
✗ Branch 1 not taken.
351 innobase_discard_table(m_user_thd, ctx->old_table);
7930 }
7931
7932
1/2
✓ Branch 0 taken 351 times.
✗ Branch 1 not taken.
351 row_mysql_unlock_data_dictionary(trx);
7933 351 MONITOR_ATOMIC_DEC(MONITOR_PENDING_ALTER_TABLE);
7934 351 return false;
7935 }
7936
7937
2/6
✓ Branch 0 taken 53097 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 53097 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
53097 DBUG_EXECUTE_IF("ib_ddl_crash_after_user_trx_commit", DBUG_SUICIDE(););
7938
7939 53097 uint64_t autoinc = 0;
7940
2/2
✓ Branch 0 taken 58803 times.
✓ Branch 1 taken 53074 times.
111877 for (inplace_alter_handler_ctx **pctx = ctx_array; *pctx; pctx++) {
7941 58803 ha_innobase_inplace_ctx *ctx =
7942 static_cast<ha_innobase_inplace_ctx *>(*pctx);
7943
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 58803 times.
58803 assert(ctx->need_rebuild() == new_clustered);
7944
7945
2/2
✓ Branch 0 taken 4357 times.
✓ Branch 1 taken 54446 times.
58803 if (altered_table->found_next_number_field) {
7946
2/2
✓ Branch 0 taken 4169 times.
✓ Branch 1 taken 188 times.
4357 if (ctx->max_autoinc > autoinc) {
7947 4169 autoinc = ctx->max_autoinc;
7948 }
7949
7950 4357 dict_table_t *t = ctx->new_table;
7951 4357 Field *field = altered_table->found_next_number_field;
7952
7953
1/2
✓ Branch 0 taken 4357 times.
✗ Branch 1 not taken.
4357 dict_table_autoinc_lock(t);
7954
1/2
✓ Branch 0 taken 4357 times.
✗ Branch 1 not taken.
4357 dict_table_autoinc_initialize(t, ctx->max_autoinc);
7955 4357 t->autoinc_persisted = ctx->max_autoinc - 1;
7956
1/2
✓ Branch 0 taken 4357 times.
✗ Branch 1 not taken.
4357 dict_table_autoinc_set_col_pos(t, field->field_index());
7957
1/2
✓ Branch 0 taken 4357 times.
✗ Branch 1 not taken.
4357 dict_table_autoinc_unlock(t);
7958 }
7959
7960 58803 bool add_fts = false;
7961
7962 /* Publish the created fulltext index, if any.
7963 Note that a fulltext index can be created without
7964 creating the clustered index, if there already exists
7965 a suitable FTS_DOC_ID column. If not, one will be
7966 created, implying new_clustered */
7967
2/2
✓ Branch 0 taken 54200 times.
✓ Branch 1 taken 58803 times.
113003 for (ulint i = 0; i < ctx->num_to_add_index; i++) {
7968 54200 dict_index_t *index = ctx->add_index[i];
7969
7970
2/2
✓ Branch 0 taken 416 times.
✓ Branch 1 taken 53784 times.
54200 if (index->type & DICT_FTS) {
7971
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 416 times.
416 assert(index->type == DICT_FTS);
7972 /* We reset DICT_TF2_FTS here because the bit
7973 is left unset when a drop proceeds the add. */
7974 416 DICT_TF2_FLAG_SET(ctx->new_table, DICT_TF2_FTS);
7975
1/2
✓ Branch 0 taken 416 times.
✗ Branch 1 not taken.
416 fts_add_index(index, ctx->new_table);
7976 416 add_fts = true;
7977 }
7978 }
7979
7980
1/2
✓ Branch 0 taken 58803 times.
✗ Branch 1 not taken.
58803 ut_d(dict_table_check_for_dup_indexes(ctx->new_table, CHECK_ALL_COMPLETE));
7981
7982
4/4
✓ Branch 0 taken 416 times.
✓ Branch 1 taken 58387 times.
✓ Branch 2 taken 296 times.
✓ Branch 3 taken 120 times.
58803 if (add_fts && !ctx->new_table->discard_after_ddl) {
7983
1/2
✓ Branch 0 taken 296 times.
✗ Branch 1 not taken.
296 fts_optimize_add_table(ctx->new_table);
7984 }
7985
7986
1/2
✓ Branch 0 taken 58803 times.
✗ Branch 1 not taken.
58803 ut_d(dict_table_check_for_dup_indexes(ctx->new_table, CHECK_ABORTED_OK));
7987
2/4
✓ Branch 0 taken 58803 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 58803 times.
58803 ut_a(fts_check_cached_index(ctx->new_table));
7988
7989
2/2
✓ Branch 0 taken 35616 times.
✓ Branch 1 taken 23187 times.
58803 if (new_clustered) {
7990 /* Since the table has been rebuilt, we remove
7991 all persistent statistics corresponding to the
7992 old copy of the table (which was renamed to
7993 ctx->tmp_name). */
7994
7995 char errstr[ERROR_STR_LENGTH];
7996
7997
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 35616 times.
35616 assert(0 == strcmp(ctx->old_table->name.m_name, ctx->tmp_name));
7998
7999
4/6
✓ Branch 0 taken 35616 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 35615 times.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
35616 DBUG_EXECUTE_IF("ib_rename_index_fail3",
8000 DBUG_SET("+d,innodb_report_deadlock"););
8001
8002
1/2
✓ Branch 0 taken 35616 times.
✗ Branch 1 not taken.
35616 if (dict_stats_drop_table(ctx->new_table->name.m_name, errstr,
8003
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 35615 times.
35616 sizeof(errstr)) != DB_SUCCESS) {
8004 1 push_warning_printf(m_user_thd, Sql_condition::SL_WARNING,
8005 ER_ALTER_INFO,
8006 "Deleting persistent statistics"
8007 " for rebuilt table '%s' in"
8008 " InnoDB failed: %s",
8009
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 table->s->table_name.str, errstr);
8010 }
8011
8012
4/6
✓ Branch 0 taken 35616 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 35615 times.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
35616 DBUG_EXECUTE_IF("ib_rename_index_fail3",
8013 DBUG_SET("-d,innodb_report_deadlock"););
8014
8015
2/6
✓ Branch 0 taken 35616 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 35616 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
35616 DBUG_EXECUTE_IF("ib_ddl_crash_before_commit", DBUG_SUICIDE(););
8016
8017
4/6
✓ Branch 0 taken 30672 times.
✓ Branch 1 taken 4944 times.
✓ Branch 2 taken 30672 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 35616 times.
35616 ut_ad(m_prebuilt != ctx->prebuilt || ctx == ctx0);
8018 35616 bool update_own_prebuilt = (m_prebuilt == ctx->prebuilt);
8019 35616 trx_t *const user_trx = m_prebuilt->trx;
8020
3/4
✓ Branch 0 taken 35616 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5970 times.
✓ Branch 3 taken 29646 times.
35616 if (dict_table_is_partition(ctx->new_table)) {
8021 /* Set blob_heap to NULL for partitioned tables to avoid
8022 row_prebuilt_free() from freeing them. We do this to avoid double free
8023 of blob_heap since all partitions point to the same blob_heap in
8024 prebuilt. Blob heaps of all the partitions will be freed later in the
8025 ha_innopart::clear_blob_heaps() */
8026 5970 ctx->prebuilt->blob_heap = nullptr;
8027 }
8028
8029
1/2
✓ Branch 0 taken 35616 times.
✗ Branch 1 not taken.
35616 row_prebuilt_free(ctx->prebuilt, true);
8030
8031 /* Drop the copy of the old table, which was
8032 renamed to ctx->tmp_name at the atomic DDL
8033 transaction commit. If the system crashes
8034 before this is completed, some orphan tables
8035 with ctx->tmp_name may be recovered. */
8036 35616 ddl::drop_table(trx, ctx->old_table);
8037
8038 /* Rebuild the prebuilt object. */
8039 35593 ctx->prebuilt =
8040
1/2
✓ Branch 0 taken 35593 times.
✗ Branch 1 not taken.
35593 row_create_prebuilt(ctx->new_table, altered_table->s->reclength);
8041
2/2
✓ Branch 0 taken 30653 times.
✓ Branch 1 taken 4940 times.
35593 if (update_own_prebuilt) {
8042 30653 m_prebuilt = ctx->prebuilt;
8043 }
8044 35593 user_trx->will_lock++;
8045 35593 m_prebuilt->trx = user_trx;
8046 }
8047
2/6
✓ Branch 0 taken 58780 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 58780 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
58780 DBUG_INJECT_CRASH("ib_commit_inplace_crash", crash_inject_count++);
8048 }
8049
8050
1/2
✓ Branch 0 taken 53074 times.
✗ Branch 1 not taken.
53074 row_mysql_unlock_data_dictionary(trx);
8051
8052
2/2
✓ Branch 0 taken 4110 times.
✓ Branch 1 taken 48964 times.
53074 if (altered_table->found_next_number_field != nullptr) {
8053
2/4
✓ Branch 0 taken 4110 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4110 times.
✗ Branch 3 not taken.
4110 dd_set_autoinc(new_dd_tab->se_private_data(), autoinc);
8054 }
8055
8056
4/6
✓ Branch 0 taken 53074 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 53072 times.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
53074 DBUG_EXECUTE_IF("ib_ddl_crash_before_update_stats", DBUG_SUICIDE(););
8057
8058 /* Rebuild index translation table now for temporary tables if we are
8059 restoring secondary keys, as ha_innobase::open will not be called for
8060 the next access. */
8061
4/4
✓ Branch 0 taken 13 times.
✓ Branch 1 taken 53059 times.
✓ Branch 2 taken 6 times.
✓ Branch 3 taken 53066 times.
53085 if (DICT_TF2_FLAG_IS_SET(ctx0->new_table, DICT_TF2_TEMPORARY) &&
8062
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 7 times.
13 ctx0->num_to_add_index) {
8063
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 ut_ad(!ctx0->num_to_drop_index);
8064
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 ut_ad(!ctx0->num_to_rename);
8065
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 ut_ad(!ctx0->num_to_drop_fk);
8066
2/4
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 6 times.
6 if (!innobase_build_index_translation(altered_table, ctx0->new_table,
8067 m_share)) {
8068 MONITOR_ATOMIC_DEC(MONITOR_PENDING_ALTER_TABLE);
8069 return true;
8070 }
8071 }
8072
8073 /* TODO: The following code could be executed
8074 while allowing concurrent access to the table
8075 (MDL downgrade). */
8076
8077
2/2
✓ Branch 0 taken 30649 times.
✓ Branch 1 taken 22423 times.
53072 if (new_clustered) {
8078
2/2
✓ Branch 0 taken 35587 times.
✓ Branch 1 taken 30649 times.
66236 for (inplace_alter_handler_ctx **pctx = ctx_array; *pctx; pctx++) {
8079 35587 ha_innobase_inplace_ctx *ctx =
8080 static_cast<ha_innobase_inplace_ctx *>(*pctx);
8081
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 35587 times.
35587 assert(ctx->need_rebuild());
8082
8083
1/2
✓ Branch 0 taken 35587 times.
✗ Branch 1 not taken.
35587 alter_stats_rebuild(ctx->new_table, table->s->table_name.str, m_user_thd);
8084
2/6
✓ Branch 0 taken 35587 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 35587 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
35587 DBUG_INJECT_CRASH("ib_commit_inplace_crash", crash_inject_count++);
8085 }
8086 } else {
8087
2/2
✓ Branch 0 taken 23183 times.
✓ Branch 1 taken 22423 times.
45606 for (inplace_alter_handler_ctx **pctx = ctx_array; *pctx; pctx++) {
8088 23183 ha_innobase_inplace_ctx *ctx =
8089 static_cast<ha_innobase_inplace_ctx *>(*pctx);
8090
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23183 times.
23183 assert(!ctx->need_rebuild());
8091
8092
1/2
✓ Branch 0 taken 23183 times.
✗ Branch 1 not taken.
23183 alter_stats_norebuild(ha_alter_info, ctx, table->s->table_name.str,
8093 m_user_thd);
8094
2/6
✓ Branch 0 taken 23183 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 23183 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
23183 DBUG_INJECT_CRASH("ib_commit_inplace_crash", crash_inject_count++);
8095
8096
5/6
✓ Branch 0 taken 98 times.
✓ Branch 1 taken 23085 times.
✓ Branch 2 taken 98 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 98 times.
✓ Branch 5 taken 23085 times.
23281 if (ctx->fts_drop_aux_vec != nullptr &&
8097 98 ctx->fts_drop_aux_vec->aux_name.size() > 0) {
8098 98 fts_drop_dd_tables(ctx->fts_drop_aux_vec,
8099
2/4
✓ Branch 0 taken 98 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 98 times.
✗ Branch 3 not taken.
98 dict_table_is_file_per_table(ctx->old_table));
8100 }
8101 }
8102 }
8103
8104 /* We don't support compression for the system tablespace nor
8105 the temporary tablespace. Only because they are shared tablespaces.
8106 There is no other technical reason. */
8107
8108 53072 innobase_parse_hint_from_comment(m_user_thd, m_prebuilt->table,
8109
1/2
✓ Branch 0 taken 53072 times.
✗ Branch 1 not taken.
53072 altered_table->s);
8110
8111 /* TODO: Also perform DROP TABLE and DROP INDEX after
8112 the MDL downgrade. */
8113
8114 #ifdef UNIV_DEBUG
8115
1/2
✓ Branch 0 taken 53072 times.
✗ Branch 1 not taken.
53072 dict_index_t *clust_index = ctx0->prebuilt->table->first_index();
8116
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 53072 times.
53072 assert(!clust_index->online_log);
8117
2/4
✓ Branch 0 taken 53072 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 53072 times.
53072 assert(dict_index_get_online_status(clust_index) == ONLINE_INDEX_COMPLETE);
8118
8119
3/4
✓ Branch 0 taken 82793 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 82793 times.
✓ Branch 3 taken 53072 times.
135865 for (dict_index_t *index = clust_index; index; index = index->next()) {
8120
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 82793 times.
82793 assert(!index->to_be_dropped);
8121 }
8122 #endif /* UNIV_DEBUG */
8123 53072 MONITOR_ATOMIC_DEC(MONITOR_PENDING_ALTER_TABLE);
8124 53072 return false;
8125 64246 }
8126
8127 /** Helper class for in-place alter partitioned table, see handler.h */
8128 class ha_innopart_inplace_ctx : public inplace_alter_handler_ctx {
8129 /* Only used locally in this file, so have everything public for
8130 convenience. */
8131 public:
8132 /** Total number of partitions. */
8133 uint m_tot_parts;
8134 /** Array of inplace contexts for all partitions. */
8135 inplace_alter_handler_ctx **ctx_array;
8136 /** Array of prebuilt for all partitions. */
8137 row_prebuilt_t **prebuilt_array;
8138 /** Array of old table information needed for writing back to DD */
8139 alter_table_old_info_t *m_old_info;
8140
8141 1564 ha_innopart_inplace_ctx(uint tot_parts)
8142 1564 : inplace_alter_handler_ctx(),
8143 1564 m_tot_parts(tot_parts),
8144 1564 ctx_array(),
8145 1564 prebuilt_array(),
8146 1564 m_old_info() {}
8147
8148 2946 ~ha_innopart_inplace_ctx() override {
8149
1/2
✓ Branch 0 taken 1473 times.
✗ Branch 1 not taken.
2946 if (ctx_array) {
8150
2/2
✓ Branch 0 taken 8033 times.
✓ Branch 1 taken 1473 times.
19012 for (uint i = 0; i < m_tot_parts; i++) {
8151 16066 destroy(ctx_array[i]);
8152 }
8153 2946 ut::free(ctx_array);
8154 }
8155
8156
1/2
✓ Branch 0 taken 1473 times.
✗ Branch 1 not taken.
2946 if (m_old_info != nullptr) {
8157 2946 ut::free(m_old_info);
8158 }
8159
8160
1/2
✓ Branch 0 taken 1473 times.
✗ Branch 1 not taken.
2946 if (prebuilt_array) {
8161 /* First entry is the original prebuilt! */
8162
2/2
✓ Branch 0 taken 6560 times.
✓ Branch 1 taken 1473 times.
16066 for (uint i = 1; i < m_tot_parts; i++) {
8163 /* Don't close the tables. */
8164 13120 prebuilt_array[i]->table = nullptr;
8165 13120 row_prebuilt_free(prebuilt_array[i], false);
8166 }
8167 2946 ut::free(prebuilt_array);
8168 }
8169 }
8170 };
8171
8172 /** Helper class for encapsulating new/altered partitions during
8173 ADD(HASH/KEY)/COALESCE/REORGANIZE PARTITION. Here as many partition slots
8174 as in new table would be created, it's OK for ADD/COALESCE PARTITION,
8175 however more partition slots would probably be created for REORGANIZE PARTITION.
8176 Considering that it's easy to get table in this way, it's still OK. */
8177 class Altered_partitions {
8178 public:
8179 /** Constructor
8180 @param[in] parts total partitions */
8181 648 Altered_partitions(uint parts)
8182 648 : m_new_table_parts(),
8183 648 m_ins_nodes(),
8184 648 m_sql_stat_start(),
8185 648 m_trx_ids(),
8186 648 m_num_new_parts(parts) {}
8187
8188 /** Destructor */
8189 ~Altered_partitions();
8190
8191 /** Initialize the object.
8192 @return false on success
8193 @retval true on failure */
8194 bool initialize();
8195
8196 /** Open and set currently used partition.
8197 @param[in] new_part_id Partition id to set.
8198 @param[in,out] part Internal table object to use. */
8199 1733 void set_part(ulint new_part_id, dict_table_t *part) {
8200
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1733 times.
1733 ut_ad(m_new_table_parts[new_part_id] == nullptr);
8201 1733 m_new_table_parts[new_part_id] = part;
8202 1733 part->skip_alter_undo = true;
8203 1733 m_sql_stat_start.set(new_part_id);
8204 1733 }
8205
8206 /** Get lower level internal table object for partition.
8207 @param[in] part_id Partition id.
8208 @return Lower level internal table object for the partition id. */
8209 3255 dict_table_t *part(uint part_id) {
8210
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3255 times.
3255 ut_ad(part_id < m_num_new_parts);
8211 3255 return (m_new_table_parts[part_id]);
8212 }
8213
8214 /** To write a row, set up prebuilt for using a specified partition.
8215 @param[in,out] prebuilt Prebuilt to update.
8216 @param[in] new_part_id Partition to use. */
8217 3250 void prepare_write(row_prebuilt_t *prebuilt, uint new_part_id) const {
8218
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3250 times.
3250 ut_ad(m_new_table_parts[new_part_id]);
8219 3250 prebuilt->table = m_new_table_parts[new_part_id];
8220 3250 prebuilt->ins_node = m_ins_nodes[new_part_id];
8221 3250 prebuilt->trx_id = m_trx_ids[new_part_id];
8222 3250 prebuilt->sql_stat_start = m_sql_stat_start.test(new_part_id);
8223 3250 }
8224
8225 /** After a write, update cached values for a partition from prebuilt.
8226 @param[in,out] prebuilt Prebuilt to copy from.
8227 @param[in] new_part_id Partition id to copy. */
8228 3250 void finish_write(row_prebuilt_t *prebuilt, uint new_part_id) {
8229
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3250 times.
3250 ut_ad(m_new_table_parts[new_part_id] == prebuilt->table);
8230 3250 m_ins_nodes[new_part_id] = prebuilt->ins_node;
8231 3250 m_trx_ids[new_part_id] = prebuilt->trx_id;
8232
1/2
✓ Branch 0 taken 3250 times.
✗ Branch 1 not taken.
3250 if (!prebuilt->sql_stat_start) {
8233 3250 m_sql_stat_start.set(new_part_id, false);
8234 }
8235 3250 }
8236
8237 private:
8238 /** New partitions created during ADD(HASH/KEY)/COALESCE/REORGANIZE
8239 PARTITION. */
8240 dict_table_t **m_new_table_parts;
8241
8242 /** Insert nodes per partition. */
8243 ins_node_t **m_ins_nodes;
8244
8245 /** bytes for sql_stat_start bitset */
8246 byte *m_bitset;
8247
8248 /** sql_stat_start per partition */
8249 Sql_stat_start_parts m_sql_stat_start;
8250
8251 /** Trx id per partition. */
8252 trx_id_t *m_trx_ids;
8253
8254 /** Number of new partitions. */
8255 size_t m_num_new_parts;
8256 };
8257
8258 /** Destructor */
8259 539 Altered_partitions::~Altered_partitions() {
8260
1/2
✓ Branch 0 taken 539 times.
✗ Branch 1 not taken.
539 if (m_new_table_parts != nullptr) {
8261
2/2
✓ Branch 0 taken 2279 times.
✓ Branch 1 taken 539 times.
2818 for (ulint i = 0; i < m_num_new_parts; i++) {
8262
2/2
✓ Branch 0 taken 1556 times.
✓ Branch 1 taken 723 times.
2279 if (m_new_table_parts[i] != nullptr) {
8263 1556 m_new_table_parts[i]->skip_alter_undo = false;
8264 }
8265 }
8266
8267 539 ut::free(m_new_table_parts);
8268 }
8269
8270
1/2
✓ Branch 0 taken 539 times.
✗ Branch 1 not taken.
539 if (m_ins_nodes != nullptr) {
8271
2/2
✓ Branch 0 taken 2279 times.
✓ Branch 1 taken 539 times.
2818 for (ulint i = 0; i < m_num_new_parts; i++) {
8272
2/2
✓ Branch 0 taken 939 times.
✓ Branch 1 taken 1340 times.
2279 if (m_ins_nodes[i] != nullptr) {
8273 939 ins_node_t *ins = m_ins_nodes[i];
8274
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 939 times.
939 ut_ad(ins->select == nullptr);
8275 939 que_graph_free_recursive(ins->select);
8276 939 ins->select = nullptr;
8277
1/2
✓ Branch 0 taken 939 times.
✗ Branch 1 not taken.
939 if (ins->entry_sys_heap != nullptr) {
8278 939 mem_heap_free(ins->entry_sys_heap);
8279 939 ins->entry_sys_heap = nullptr;
8280 }
8281 }
8282 }
8283
8284 539 ut::free(m_ins_nodes);
8285 }
8286
8287 539 ut::free(m_bitset);
8288 539 ut::free(m_trx_ids);
8289 539 }
8290
8291 /** Initialize the object.
8292 @return false on success else true. */
8293 648 bool Altered_partitions::initialize() {
8294 648 size_t alloc_size = sizeof(*m_new_table_parts) * m_num_new_parts;
8295 648 m_new_table_parts = static_cast<dict_table_t **>(ut::zalloc_withkey(
8296 ut::make_psi_memory_key(mem_key_partitioning), alloc_size));
8297
8298 648 alloc_size = sizeof(*m_ins_nodes) * m_num_new_parts;
8299 648 m_ins_nodes = static_cast<ins_node_t **>(ut::zalloc_withkey(
8300 ut::make_psi_memory_key(mem_key_partitioning), alloc_size));
8301
8302 648 alloc_size = sizeof(*m_bitset) * UT_BITS_IN_BYTES(m_num_new_parts);
8303 648 m_bitset = static_cast<byte *>(ut::zalloc_withkey(
8304 ut::make_psi_memory_key(mem_key_partitioning), alloc_size));
8305
8306 648 alloc_size = sizeof(*m_trx_ids) * m_num_new_parts;
8307 648 m_trx_ids = static_cast<trx_id_t *>(ut::zalloc_withkey(
8308 ut::make_psi_memory_key(mem_key_partitioning), alloc_size));
8309
8310
2/4
✓ Branch 0 taken 648 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 648 times.
✗ Branch 3 not taken.
648 if (m_new_table_parts == nullptr || m_ins_nodes == nullptr ||
8311
2/4
✓ Branch 0 taken 648 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 648 times.
648 m_bitset == nullptr || m_trx_ids == nullptr) {
8312 ut::free(m_new_table_parts);
8313 ut::free(m_ins_nodes);
8314 ut::free(m_bitset);
8315 ut::free(m_trx_ids);
8316
8317 return (true);
8318 }
8319
8320 648 m_sql_stat_start.init(m_bitset, UT_BITS_IN_BYTES(m_num_new_parts));
8321
8322 648 return (false);
8323 }
8324
8325 /** Class(interface) which manages the operations for partitions of states
8326 in different categories during ALTER PARTITION. There are four categories
8327 for now:
8328 1. normal: mapping to PART_NORMAL, which means the partition is not changed
8329 2. add: mapping to PART_TO_BE_ADDED
8330 3. drop: mapping to PART_TO_BE_DROPPED, PART_TO_BE_REORGED
8331 and PART_REORGED_DROPPED
8332 4. change: mapping to PART_CHANGED */
8333 class alter_part {
8334 public:
8335 /** Virtual destructor */
8336 10188 virtual ~alter_part() = default;
8337
8338 /** Return the partition id */
8339 6489 virtual uint part_id() const { return (m_part_id); }
8340
8341 /** Return the partition state */
8342 10382 virtual partition_state state() const { return (m_state); }
8343
8344 /** Get the InnoDB table object for newly created partition
8345 if applicable
8346 @return the InnoDB table object or nullptr if not applicable */
8347 4217 dict_table_t *new_table() { return (m_new); }
8348
8349 /** Set the freed old partition to nullptr to avoid dangling pointer
8350 @param check_in_cache whether we need to check table in cache
8351 @param part_name Partitioned table name .*/
8352 2179 inline void free_old_part(bool check_in_cache, const char *part_name) {
8353
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2179 times.
2179 if (check_in_cache) {
8354 dict_sys_mutex_enter();
8355
8356 if (!dict_table_check_if_in_cache_low(part_name)) {
8357 *m_old = nullptr;
8358 }
8359
8360 dict_sys_mutex_exit();
8361
8362 } else {
8363 2179 *m_old = nullptr;
8364 }
8365 2179 }
8366
8367 /** Prepare
8368 @param[in,out] altered_table Table definition after the ALTER
8369 @param[in] old_part the stored old partition or nullptr
8370 if no corresponding one exists
8371 @param[in,out] new_part the stored new partition or nullptr
8372 if no corresponding one exists
8373 @return 0 or error number */
8374 1410 virtual int prepare(TABLE *altered_table [[maybe_unused]],
8375 const dd::Partition *old_part [[maybe_unused]],
8376 dd::Partition *new_part [[maybe_unused]]) {
8377 1410 return (0);
8378 }
8379
8380 /** Try to commit
8381 @param[in] table Table definition before the ALTER
8382 @param[in,out] altered_table Table definition after the ALTER
8383 @param[in] old_part the stored old partition or nullptr
8384 if no corresponding one exists
8385 @param[in,out] new_part the stored new partition or nullptr
8386 if no corresponding one exists
8387 @return 0 or error number */
8388 virtual int try_commit(const TABLE *table [[maybe_unused]],
8389 TABLE *altered_table [[maybe_unused]],
8390 const dd::Partition *old_part [[maybe_unused]],
8391 dd::Partition *new_part [[maybe_unused]]) {
8392 return (0);
8393 }
8394
8395 /** Rollback */
8396 174 virtual void rollback() { return; }
8397
8398 protected:
8399 /** Constructor
8400 @param[in,out] trx InnoDB transaction, nullptr if not used
8401 @param[in] part_id Partition id in the table. This could
8402 be partition id for either old table
8403 or new table, callers should remember
8404 which one is applicable
8405 @param[in] state Partition state of the partition on
8406 which this class will do operations.
8407 If this is for one partition in new
8408 table, the partition state is the same
8409 for both the new partition and the
8410 corresponding old partition
8411 @param[in] table_name Partitioned table name, in the
8412 form of db/table, which considers
8413 the charset
8414 @param[in,out] old InnoDB table object for old partition,
8415 default is nullptr, which means there
8416 is no corresponding object */
8417 5613 alter_part(trx_t *trx, uint part_id, partition_state state,
8418 const char *table_name, dict_table_t **old)
8419 5613 : m_trx(trx),
8420 5613 m_part_id(part_id),
8421 5613 m_state(state),
8422 5613 m_table_name(table_name),
8423 5613 m_old(old),
8424 5613 m_new(nullptr) {}
8425
8426 /** Build the partition name for specified partition
8427 @param[in] dd_part dd::Partition
8428 @param[in] temp True if this is a temporary name
8429 @param[out] name Partition name buffer of length FN_REFLEN
8430 @return true if successful. */
8431 bool build_partition_name(const dd::Partition *dd_part, bool temp,
8432 char *name);
8433
8434 /** Create a new partition
8435 @param[in] part_table partition table
8436 @param[in] part_name Partition name, including db/table
8437 @param[in,out] dd_part dd::Partition
8438 @param[in] table Table format
8439 @param[in] tablespace Tablespace of this partition,
8440 if length is 0, it means no
8441 tablespace specified
8442 @param[in] file_per_table Current value of innodb_file_per_table
8443 @param[in] autoinc Next AUTOINC value to use
8444 @param[in] autoextend_size Value of AUTOEXTEND_SIZE for this tablespace
8445 @return 0 or error number */
8446 int create(const dd::Table *part_table, const char *part_name,
8447 dd::Partition *dd_part, TABLE *table, const char *tablespace,
8448 bool file_per_table, uint64_t autoinc, uint64_t autoextend_size);
8449
8450 protected:
8451 /** InnoDB transaction, nullptr if not used */
8452 trx_t *const m_trx;
8453
8454 /** Partition id in the table. This could be partition id for
8455 either old table or new table, callers should remember which one
8456 is applicable */
8457 uint m_part_id;
8458
8459 /** Partition state of the partition on which this class will
8460 do operations. If this is for one partition in new table, the
8461 partition state is the same for both the new partition and the
8462 corresponding old partition */
8463 partition_state m_state;
8464
8465 /** Partitioned table name, in form of ./db/table, which already
8466 considers the charset */
8467 const char *m_table_name;
8468
8469 /** The InnoDB table object for old partition */
8470 dict_table_t **m_old;
8471
8472 /** The InnoDB table object for newly created partition */
8473 dict_table_t *m_new;
8474 };
8475
8476 5178 bool alter_part::build_partition_name(const dd::Partition *dd_part, bool temp,
8477 char *name) {
8478
2/4
✓ Branch 0 taken 5178 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 5178 times.
5178 if (!normalize_table_name(name, m_table_name)) {
8479 /* purecov: begin inspected */
8480 ut_d(ut_error);
8481 ut_o(return (false));
8482 /* purecov: end */
8483 }
8484
8485 5178 std::string partition;
8486 /* Build the partition name. */
8487
1/2
✓ Branch 0 taken 5178 times.
✗ Branch 1 not taken.
5178 dict_name::build_partition(dd_part, partition);
8488
8489 5178 std::string partition_name;
8490 /* Build the partitioned table name. */
8491
3/6
✓ Branch 0 taken 5178 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5178 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 5178 times.
✗ Branch 5 not taken.
5178 dict_name::build_table("", name, partition, temp, false, partition_name);
8492
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5178 times.
5178 ut_ad(partition_name.length() < FN_REFLEN);
8493
8494 /* Copy partition table name. */
8495
1/2
✓ Branch 0 taken 5178 times.
✗ Branch 1 not taken.
5178 auto name_len = partition_name.copy(name, FN_REFLEN - 1);
8496 5178 name[name_len] = '\0';
8497
8498 5178 return (true);
8499 5178 }
8500
8501 1932 int alter_part::create(const dd::Table *old_part_table, const char *part_name,
8502 dd::Partition *dd_part, TABLE *table,
8503 const char *tablespace, bool file_per_table,
8504 uint64_t autoinc, uint64_t autoextend_size) {
8505
4/6
✓ Branch 0 taken 1051 times.
✓ Branch 1 taken 881 times.
✓ Branch 2 taken 1051 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 1932 times.
1932 ut_ad(m_state == PART_TO_BE_ADDED || m_state == PART_CHANGED);
8506
8507
1/2
✓ Branch 0 taken 1932 times.
✗ Branch 1 not taken.
1932 dd::Table &dd_table = dd_part->table();
8508
1/2
✓ Branch 0 taken 1932 times.
✗ Branch 1 not taken.
1932 dd::Properties &options = dd_table.options();
8509 uint32_t key_block_size;
8510
3/6
✓ Branch 0 taken 1932 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1932 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 1932 times.
1932 ut_ad(options.exists("key_block_size"));
8511
2/4
✓ Branch 0 taken 1932 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1932 times.
✗ Branch 3 not taken.
1932 options.get("key_block_size", &key_block_size);
8512
8513
1/2
✓ Branch 0 taken 1932 times.
✗ Branch 1 not taken.
1932 dd::Properties &part_options = dd_part->options();
8514 1932 dd::String_type data_file_name;
8515
3/4
✓ Branch 0 taken 1932 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 26 times.
✓ Branch 3 taken 1906 times.
1932 if (part_options.exists(data_file_name_key))
8516
1/2
✓ Branch 0 taken 26 times.
✗ Branch 1 not taken.
26 (void)part_options.get(data_file_name_key, &data_file_name);
8517 /* index_file_name is not allowed for now */
8518 char full_path[FN_REFLEN];
8519
2/2
✓ Branch 0 taken 26 times.
✓ Branch 1 taken 1906 times.
1932 if (!data_file_name.empty()) {
8520 /* Have to append the postfix table name, to make it work */
8521 26 const char *name = strrchr(part_name, '/');
8522
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 26 times.
26 ut_ad(name != nullptr);
8523 26 size_t len = data_file_name.length();
8524 26 strcpy(full_path, data_file_name.c_str());
8525 26 full_path[len] = OS_PATH_SEPARATOR;
8526 26 strcpy(full_path + len + 1, name + 1);
8527 }
8528
8529
1/2
✓ Branch 0 taken 1932 times.
✗ Branch 1 not taken.
1932 HA_CREATE_INFO create_info;
8530
1/2
✓ Branch 0 taken 1932 times.
✗ Branch 1 not taken.
1932 update_create_info_from_table(&create_info, table);
8531 1932 create_info.auto_increment_value = autoinc;
8532 1932 create_info.key_block_size = key_block_size;
8533
2/2
✓ Branch 0 taken 1906 times.
✓ Branch 1 taken 26 times.
1932 create_info.data_file_name = data_file_name.empty() ? nullptr : full_path;
8534
2/2
✓ Branch 0 taken 39 times.
✓ Branch 1 taken 1893 times.
1932 create_info.tablespace = tablespace[0] == '\0' ? nullptr : tablespace;
8535 1932 create_info.m_implicit_tablespace_autoextend_size = autoextend_size;
8536
8537 /* The below check is the same as for CREATE TABLE, but since we are
8538 doing an alter here it will not trigger the check in
8539 create_option_tablespace_is_valid(). */
8540 1932 if (tablespace_is_shared_space(&create_info) &&
8541
2/6
✗ Branch 0 not taken.
✓ Branch 1 taken 1932 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 1932 times.
1932 create_info.data_file_name != nullptr &&
8542 create_info.data_file_name[0] != '\0') {
8543 my_printf_error(ER_ILLEGAL_HA_CREATE_OPTION,
8544 "InnoDB: DATA DIRECTORY cannot be used"
8545 " with a TABLESPACE assignment.",
8546 MYF(0));
8547 return (HA_WRONG_CREATE_OPTION);
8548 }
8549
8550 5721 return (innobase_basic_ddl::create_impl<dd::Partition>(
8551
2/4
✓ Branch 0 taken 1932 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1857 times.
✗ Branch 3 not taken.
1932 current_thd, part_name, table, &create_info, dd_part, file_per_table,
8552 1857 false, false, 0, 0, old_part_table));
8553 1857 }
8554
8555 typedef std::vector<alter_part *, ut::allocator<alter_part *>> alter_part_array;
8556
8557 /** Construct all necessary alter_part_* objects according to the given
8558 partition states in both old and new tables */
8559 class alter_part_factory {
8560 public:
8561 /** Constructor
8562 @param[in,out] trx Transaction
8563 @param[in] ha_alter_info ALTER Information
8564 @param[in,out] part_share Innopart share
8565 @param[in] old_part_info Partition info of the table before
8566 ALTER TABLE */
8567 1000 alter_part_factory(trx_t *trx, const Alter_inplace_info *ha_alter_info,
8568 Ha_innopart_share *part_share,
8569 partition_info *old_part_info)
8570 1000 : m_trx(trx),
8571 1000 m_part_share(part_share),
8572 1000 m_ha_alter_info(ha_alter_info),
8573 1000 m_old_part_info(old_part_info),
8574 1000 m_file_per_table(srv_file_per_table) {}
8575
8576 /** Destructor */
8577 ~alter_part_factory() = default;
8578
8579 /** Create the alter_part_* objects according to the given
8580 partition states
8581 @param[in,out] to_drop To store the alter_part_* objects
8582 for partitions to be dropped
8583 @param[in,out] all_news To store the alter_part_* objects
8584 for partitions in table after
8585 ALTER TABLE
8586 @return false On success
8587 @retval true On failure */
8588 1000 bool create(alter_part_array &to_drop, alter_part_array &all_news) {
8589 1000 to_drop.clear();
8590 1000 all_news.clear();
8591
8592
2/2
✓ Branch 0 taken 769 times.
✓ Branch 1 taken 231 times.
1000 if (!(m_ha_alter_info->handler_flags &
8593 Alter_inplace_info::REORGANIZE_PARTITION)) {
8594 769 return (create_for_non_reorg(to_drop, all_news));
8595 } else {
8596 231 return (create_for_reorg(to_drop, all_news));
8597 }
8598 }
8599
8600 private:
8601 bool create_for_reorg(alter_part_array &to_drop, alter_part_array &all_news);
8602 bool create_for_non_reorg(alter_part_array &to_drop,
8603 alter_part_array &all_news);
8604 bool create_new_checking_conflict(partition_element *new_part,
8605 uint &new_part_id,
8606 alter_part_array &all_news);
8607 bool create_old_checking_conflict(partition_element *old_part,
8608 uint &old_part_id,
8609 alter_part_array &to_drop);
8610 bool is_conflict(const partition_element *new_part,
8611 const partition_element *old_part);
8612 bool create_one(alter_part_array &array, partition_element *part,
8613 uint &part_id, uint old_part_id, partition_state state,
8614 bool conflict);
8615 alter_part *create_one_low(uint &part_id, uint old_part_id,
8616 partition_state state, const char *tablespace,
8617 bool conflict);
8618
8619 private:
8620 /** InnoDB transaction */
8621 trx_t *const m_trx;
8622
8623 /** InnoDB partition specific Handler_share */
8624 Ha_innopart_share *const m_part_share;
8625
8626 /** ALTER information */
8627 const Alter_inplace_info *const m_ha_alter_info;
8628
8629 /** Partition info of the table before ALTER TABLE */
8630 partition_info *const m_old_part_info;
8631
8632 /** Current innodb_file_per_table value */
8633 bool m_file_per_table;
8634 };
8635
8636 /** Helper class for in-place alter partitions, see handler.h */
8637 class alter_parts : public inplace_alter_handler_ctx {
8638 public:
8639 /** Constructor
8640 @param[in,out] trx InnoDB transaction
8641 @param[in,out] part_share Innopart share
8642 @param[in] ha_alter_info ALTER information
8643 @param[in] old_part_info Partition info of the table before
8644 ALTER TABLE
8645 @param[in,out] new_partitions Altered partition helper */
8646 1000 alter_parts(trx_t *trx, Ha_innopart_share *part_share,
8647 const Alter_inplace_info *ha_alter_info,
8648 partition_info *old_part_info, Altered_partitions *new_partitions)
8649 2000 : m_trx(trx),
8650 1000 m_part_share(part_share),
8651 1000 m_ha_alter_info(ha_alter_info),
8652 1000 m_new_partitions(new_partitions),
8653 1000 m_factory(trx, ha_alter_info, part_share, old_part_info),
8654
1/2
✓ Branch 0 taken 1000 times.
✗ Branch 1 not taken.
1000 m_news(),
8655
1/2
✓ Branch 0 taken 1000 times.
✗ Branch 1 not taken.
2000 m_to_drop() {}
8656
8657 /** Destructor */
8658 ~alter_parts() override;
8659
8660 /** Create the to be created partitions and update internal
8661 structures with concurrent writes blocked, while preparing
8662 ALTER TABLE.
8663 @param[in] old_dd_tab dd::Table before ALTER TABLE
8664 @param[in,out] new_dd_tab dd::Table after ALTER TABLE
8665 @param[in,out] altered_table Table definition after the ALTER
8666 @return 0 or error number, my_error() should be called by callers */
8667 int prepare(const dd::Table &old_dd_tab, dd::Table &new_dd_tab,
8668 TABLE *altered_table);
8669
8670 /** Notify the storage engine that the changes made during
8671 prepare_inplace_alter_table() and inplace_alter_table()
8672 will be rolled back for all the partitions. */
8673 void rollback();
8674
8675 /** Try to commit the changes made during prepare_inplace_alter_table()
8676 inside the storage engine. This is protected by MDL_EXCLUSIVE.
8677 @param[in] old_dd_tab dd::Table before ALTER TABLE
8678 @param[in,out] new_dd_tab dd::Table after ALTER TABLE
8679 @param[in] table Table definition before the ALTER
8680 @param[in,out] altered_table Table definition after the ALTER
8681 @return 0 or error number, my_error() should be called by callers */
8682 int try_commit(const dd::Table &old_dd_tab, dd::Table &new_dd_tab,
8683 const TABLE *table, TABLE *altered_table);
8684
8685 /** Determine if this is an ALTER TABLE ... PARTITION operation
8686 @param[in] ha_alter_info thd DDL operation
8687 @return whether it is a such kind of operation */
8688 14771 static inline bool apply_to(const Alter_inplace_info *ha_alter_info) {
8689 14771 return ((ha_alter_info->handler_flags & OPERATIONS) != 0);
8690 }
8691
8692 /** Determine if copying data between partitions is necessary
8693 @param[in] ha_alter_info thd DDL operation
8694 @return whether it is necessary to copy data */
8695 4425 static inline bool need_copy(const Alter_inplace_info *ha_alter_info) {
8696
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4425 times.
4425 ut_ad(apply_to(ha_alter_info));
8697
8698 /* Basically, only DROP PARTITION, ADD PARTITION for RANGE/LIST
8699 partitions don't require copying data between partitions */
8700
2/2
✓ Branch 0 taken 1223 times.
✓ Branch 1 taken 3202 times.
4425 if (ha_alter_info->handler_flags & Alter_inplace_info::ADD_PARTITION) {
8701
2/2
✓ Branch 0 taken 389 times.
✓ Branch 1 taken 834 times.
1223 switch (ha_alter_info->modified_part_info->part_type) {
8702 389 case partition_type::RANGE:
8703 case partition_type::LIST:
8704 389 return (false);
8705 834 default:
8706 834 break;
8707 }
8708 }
8709
8710 return (
8711 4036 !(ha_alter_info->handler_flags & (Alter_inplace_info::DROP_PARTITION)));
8712 }
8713
8714 private:
8715 /** Initialize the m_news and m_to_drop array here
8716 @param[in] old_dd_tab dd::Table before ALTER TABLE
8717 @param[in] new_dd_tab dd::Table after ALTER TABLE
8718 @retval true if success
8719 @retval false on failure */
8720 bool prepare_alter_part(const dd::Table &old_dd_tab, dd::Table &new_dd_tab);
8721
8722 /** Prepare or commit for all the partitions in table after ALTER TABLE
8723 @param[in] old_dd_tab dd::Table before ALTER TABLE
8724 @param[in,out] new_dd_tab dd::Table after ALTER TABLE
8725 @param[in,out] altered_table Table definition after the ALTER
8726 @param[in] prepare true if it's in prepare phase,
8727 false if it's in commit phase
8728 @return 0 or error number */
8729 int prepare_or_commit_for_new(const dd::Table &old_dd_tab,
8730 dd::Table &new_dd_tab, TABLE *altered_table,
8731 bool prepare);
8732
8733 /** Prepare or commit for all the partitions in table before ALTER TABLE
8734 @param[in] old_dd_tab dd::Table before ALTER TABLE
8735 @param[in,out] altered_table Table definition after the ALTER
8736 @param[in] prepare true if it's in prepare phase,
8737 false if it's in commit phase
8738 @return 0 or error number */
8739 int prepare_or_commit_for_old(const dd::Table &old_dd_tab,
8740 TABLE *altered_table, bool prepare);
8741
8742 public:
8743 /** Operations that the native partitioning can perform inplace */
8744 static constexpr Alter_inplace_info::HA_ALTER_FLAGS OPERATIONS =
8745 Alter_inplace_info::ADD_PARTITION | Alter_inplace_info::DROP_PARTITION |
8746 Alter_inplace_info::ALTER_REBUILD_PARTITION |
8747 Alter_inplace_info::COALESCE_PARTITION |
8748 Alter_inplace_info::REORGANIZE_PARTITION;
8749
8750 private:
8751 /** InnoDB transaction */
8752 trx_t *const m_trx;
8753
8754 /** InnoDB partition specific Handler_share */
8755 Ha_innopart_share *const m_part_share;
8756
8757 /** Operation being performed */
8758 const Alter_inplace_info *const m_ha_alter_info;
8759
8760 /** New partitions helper */
8761 Altered_partitions *const m_new_partitions;
8762
8763 /** alter_part factory which creates all the necessary alter_part_* */
8764 alter_part_factory m_factory;
8765
8766 /** The alter_part array for all the newly created partitions */
8767 alter_part_array m_news;
8768
8769 /** The alter_part array for all the to be dropped partitions */
8770 alter_part_array m_to_drop;
8771 };
8772
8773 /** Class which handles the partition of state PART_NORMAL.
8774 See comments for alter_part_factory::create_for_reorg
8775 and alter_part_factory::create_for_non_reorg. */
8776 class alter_part_normal : public alter_part {
8777 public:
8778 /** Constructor
8779 @param[in] part_id Partition id in the table. This could
8780 be partition id for either old table
8781 or new table, callers should remember
8782 which one is applicable
8783 @param[in] state Partition state of the partition on
8784 which this class will do operations.
8785 If this is for one partition in new
8786 table, the partition state is the same
8787 for both the new partition and the
8788 corresponding old partition
8789 @param[in,out] old InnoDB table object for old partition,
8790 default is nullptr, which means there
8791 is no corresponding object */
8792 2191 alter_part_normal(uint part_id, partition_state state, dict_table_t **old)
8793 2191 : /* Table name is not used in this class, so pass a fake
8794 one */
8795 2191 alter_part(nullptr, part_id, state, (*old)->name.m_name, old) {}
8796
8797 /** Destructor */
8798 4242 ~alter_part_normal() override = default;
8799
8800 /** Prepare
8801 @param[in,out] altered_table Table definition after the ALTER
8802 @param[in] old_part the stored old partition or nullptr
8803 if no corresponding one exists
8804 @param[in,out] new_part the stored new partition or nullptr
8805 if no corresponding one exists
8806 @return 0 or error number */
8807 2191 int prepare(TABLE *altered_table [[maybe_unused]],
8808 const dd::Partition *old_part, dd::Partition *new_part) override {
8809
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2191 times.
2191 ut_ad(old_part->name() == new_part->name());
8810
8811 2191 dd_copy_private<dd::Partition>(*new_part, *old_part);
8812
8813 2191 return (0);
8814 }
8815
8816 /** Try to commit
8817 @param[in] table Table definition before the ALTER
8818 @param[in,out] altered_table Table definition after the ALTER
8819 @param[in] old_part the stored old partition or nullptr
8820 if no corresponding one exists
8821 @param[in,out] new_part the stored new partition or nullptr
8822 if no corresponding one exists
8823 @return 0 or error number */
8824 1968 int try_commit(const TABLE *table [[maybe_unused]],
8825 TABLE *altered_table [[maybe_unused]],
8826 const dd::Partition *old_part [[maybe_unused]],
8827 dd::Partition *new_part [[maybe_unused]]) override {
8828
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1968 times.
1968 ut_ad(m_old != nullptr);
8829
8830 1968 btr_drop_ahi_for_table(*m_old);
8831
8832 1968 dict_sys_mutex_enter();
8833 1968 dd_table_close(*m_old, nullptr, nullptr, true);
8834 1968 dict_table_remove_from_cache(*m_old);
8835 1968 *m_old = nullptr;
8836 1968 dict_sys_mutex_exit();
8837 1968 return (0);
8838 }
8839 };
8840
8841 /** Class which handles the partition of the state PART_TO_BE_ADDED.
8842 See comments for alter_part_factory::create_for_reorg
8843 and alter_part_factory::create_for_non_reorg. */
8844 class alter_part_add : public alter_part {
8845 public:
8846 /** Constructor
8847 @param[in] part_id Partition id in the table. This could
8848 be partition id for either old table
8849 or new table, callers should remember
8850 which one is applicable
8851 @param[in] state Partition state of the partition on
8852 which this class will do operations.
8853 If this is for one partition in new
8854 table, the partition state is the same
8855 for both the new partition and the
8856 corresponding old partition
8857 @param[in] table_name Partitioned table name, in the form
8858 of db/table, which already considers
8859 the charset
8860 @param[in] tablespace Tablespace specified explicitly
8861 @param[in,out] trx InnoDB transaction
8862 @param[in] ha_alter_info ALTER information
8863 @param[in] file_per_table Current value of innodb_file_per_table
8864 @param[in] autoinc Next autoinc value to use
8865 @param[in] conflict True if there is already a partition
8866 table with the same name */
8867 943 alter_part_add(uint part_id, partition_state state, const char *table_name,
8868 const char *tablespace, trx_t *trx,
8869 const Alter_inplace_info *ha_alter_info, bool file_per_table,
8870 uint64_t autoinc, bool conflict)
8871 943 : alter_part(trx, part_id, state, table_name, nullptr),
8872 943 m_ha_alter_info(ha_alter_info),
8873 943 m_file_per_table(file_per_table),
8874 943 m_autoinc(autoinc),
8875 943 m_conflict(conflict) {
8876
3/4
✓ Branch 0 taken 53 times.
✓ Branch 1 taken 890 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 53 times.
943 if (tablespace == nullptr || tablespace[0] == '\0') {
8877 890 m_tablespace[0] = '\0';
8878 } else {
8879 53 strcpy(m_tablespace, tablespace);
8880 }
8881 943 }
8882
8883 /** Destructor */
8884 1592 ~alter_part_add() override = default;
8885
8886 /** Prepare
8887 @param[in,out] altered_table Table definition after the ALTER
8888 @param[in] old_part the stored old partition or nullptr
8889 if no corresponding one exists
8890 @param[in,out] new_part the stored new partition or nullptr
8891 if no corresponding one exists
8892 @return 0 or error number */
8893 889 int prepare(TABLE *altered_table, const dd::Partition *old_part,
8894 dd::Partition *new_part) override {
8895
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 889 times.
889 ut_ad(old_part != nullptr);
8896
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 889 times.
889 ut_ad(new_part != nullptr);
8897 char part_name[FN_REFLEN];
8898
8899
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 881 times.
889 if (is_shared_tablespace(m_tablespace)) {
8900
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 my_printf_error(ER_ILLEGAL_HA_CREATE_OPTION,
8901 PARTITION_IN_SHARED_TABLESPACE, MYF(0));
8902 8 return (HA_ERR_INTERNAL_ERROR);
8903 }
8904
8905
2/4
✓ Branch 0 taken 881 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 881 times.
881 if (!build_partition_name(new_part, need_rename(), part_name)) {
8906 return (HA_ERR_TOO_LONG_PATH); /* purecov: inspected */
8907 }
8908
8909 /* Get the autoextend_size value from the old partition
8910 and set this value to the partition being added. */
8911
1/2
✓ Branch 0 taken 881 times.
✗ Branch 1 not taken.
881 const dd::Table &part_table = old_part->table();
8912
8913 881 ulonglong autoextend_size{};
8914
8915
2/4
✓ Branch 0 taken 881 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 881 times.
✗ Branch 3 not taken.
881 dd::get_implicit_tablespace_options(current_thd, &part_table,
8916 &autoextend_size);
8917
8918 int error =
8919
3/4
✓ Branch 0 taken 82 times.
✓ Branch 1 taken 799 times.
✓ Branch 2 taken 842 times.
✗ Branch 3 not taken.
881 create(dd_table_has_instant_cols(part_table) ? &part_table : nullptr,
8920 881 part_name, new_part, altered_table, m_tablespace,
8921
1/2
✓ Branch 0 taken 881 times.
✗ Branch 1 not taken.
881 m_file_per_table, m_autoinc, autoextend_size);
8922
8923
7/8
✓ Branch 0 taken 838 times.
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 838 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 719 times.
✓ Branch 5 taken 119 times.
✓ Branch 6 taken 719 times.
✓ Branch 7 taken 123 times.
842 if (error == 0 && alter_parts::need_copy(m_ha_alter_info)) {
8924 /* If partition belongs to table with instant columns, copy instant
8925 metadata to new table DD */
8926
4/6
✓ Branch 0 taken 719 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 719 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 63 times.
✓ Branch 5 taken 656 times.
719 if (dd_table_has_row_versions(old_part->table())) {
8927
3/6
✓ Branch 0 taken 63 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 63 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 63 times.
✗ Branch 5 not taken.
63 inherit_instant_metadata(&old_part->table(), &new_part->table());
8928 }
8929
8930
1/2
✓ Branch 0 taken 719 times.
✗ Branch 1 not taken.
719 dict_sys_mutex_enter();
8931
1/2
✓ Branch 0 taken 719 times.
✗ Branch 1 not taken.
719 m_new = dict_table_check_if_in_cache_low(part_name);
8932
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 719 times.
719 ut_ad(m_new != nullptr);
8933
1/2
✓ Branch 0 taken 719 times.
✗ Branch 1 not taken.
719 m_new->acquire();
8934
1/2
✓ Branch 0 taken 719 times.
✗ Branch 1 not taken.
719 dict_table_ddl_release(m_new);
8935
1/2
✓ Branch 0 taken 719 times.
✗ Branch 1 not taken.
719 dict_sys_mutex_exit();
8936
8937
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 719 times.
719 return (m_new == nullptr ? DB_TABLE_NOT_FOUND : 0);
8938 }
8939
8940 123 return (error);
8941 }
8942
8943 /** Try to commit
8944 @param[in] table Table definition before the ALTER
8945 @param[in,out] altered_table Table definition after the ALTER
8946 @param[in] old_part the stored old partition or nullptr
8947 if no corresponding one exists
8948 @param[in,out] new_part the stored new partition or nullptr
8949 if no corresponding one exists
8950 @return 0 or error number */
8951 768 int try_commit(const TABLE *table [[maybe_unused]],
8952 TABLE *altered_table [[maybe_unused]],
8953 const dd::Partition *old_part [[maybe_unused]],
8954 dd::Partition *new_part) override {
8955 768 int error = 0;
8956
8957
2/2
✓ Branch 0 taken 63 times.
✓ Branch 1 taken 705 times.
768 if (need_rename()) {
8958 char old_name[FN_REFLEN];
8959 char new_name[FN_REFLEN];
8960
8961
3/6
✓ Branch 0 taken 63 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 63 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 63 times.
✗ Branch 5 not taken.
126 if (build_partition_name(new_part, true, old_name) &&
8962
2/4
✓ Branch 0 taken 63 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 63 times.
✗ Branch 3 not taken.
63 build_partition_name(new_part, false, new_name)) {
8963 60 error = innobase_basic_ddl::rename_impl<dd::Partition>(
8964
1/2
✓ Branch 0 taken 60 times.
✗ Branch 1 not taken.
63 m_trx->mysql_thd, old_name, new_name, new_part, new_part, nullptr);
8965
8966 } else {
8967 error = HA_ERR_TOO_LONG_PATH; /* purecov: inspected */
8968 }
8969 }
8970
8971
2/2
✓ Branch 0 taken 654 times.
✓ Branch 1 taken 111 times.
765 if (m_new != nullptr) {
8972 654 dd_table_close(m_new, m_trx->mysql_thd, nullptr, false);
8973 654 m_new = nullptr;
8974 }
8975
8976 765 return (error);
8977 }
8978
8979 /** Rollback */
8980 31 void rollback() override {
8981 /* Release the new table so that in post DDL, this table can be
8982 rolled back. */
8983
2/2
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 17 times.
31 if (m_new != nullptr) {
8984 14 dd_table_close(m_new, m_trx->mysql_thd, nullptr, false);
8985 14 m_new = nullptr;
8986 }
8987 31 }
8988
8989 private:
8990 /** Check if the new partition file needs a temporary name and
8991 should be renamed at last */
8992 1649 bool need_rename() const { return (m_conflict); }
8993
8994 /** Inherit instant metadata of dd::Table and dd::Columns belonging to it.
8995 This is used when a new partition is added as part of REORGANIZE partition.
8996 @param[in] source Source dd table
8997 @param[in,out] dest Destination dd table */
8998 void inherit_instant_metadata(const dd::Table *source, dd::Table *dest);
8999
9000 private:
9001 /** ALTER information */
9002 const Alter_inplace_info *m_ha_alter_info;
9003
9004 /** Current value of innodb_file_per_table */
9005 const bool m_file_per_table;
9006
9007 /** Next AUTOINC value to use */
9008 const uint64_t m_autoinc;
9009
9010 /** True if there is already a partition table with the same name */
9011 const bool m_conflict;
9012
9013 /** Tablespace of this partition */
9014 char m_tablespace[FN_REFLEN + 1];
9015 };
9016
9017 63 void alter_part_add::inherit_instant_metadata(const dd::Table *source,
9018 dd::Table *dest) {
9019 60 auto add_dropped_column = [&](const dd::Column *column) {
9020 60 const char *col_name = column->name().c_str();
9021 /* Add this column as an SE_HIDDEN column in dest table def */
9022 240 dd::Column *new_column = dd_add_hidden_column(
9023 60 dest, col_name, column->char_length(), column->type());
9024
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 60 times.
60 ut_ad(new_column != nullptr);
9025
9026 /* Copy se private data */
9027
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 60 times.
60 ut_ad(!column->se_private_data().empty());
9028 60 new_column->se_private_data().clear();
9029 60 new_column->set_se_private_data(column->se_private_data());
9030
9031 60 new_column->set_nullable(column->is_nullable());
9032 60 new_column->set_char_length(column->char_length());
9033 60 new_column->set_numeric_scale(column->numeric_scale());
9034 60 new_column->set_unsigned(column->is_unsigned());
9035 60 new_column->set_collation_id(column->collation_id());
9036 60 new_column->set_type(column->type());
9037 /* Elements for enum columns */
9038
2/4
✓ Branch 0 taken 60 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 60 times.
120 if (column->type() == dd::enum_column_types::ENUM ||
9039
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 60 times.
60 column->type() == dd::enum_column_types::SET) {
9040 for (const auto *source_elem : column->elements()) {
9041 auto *elem_obj = new_column->add_element();
9042 elem_obj->set_name(source_elem->name());
9043 }
9044 }
9045 60 };
9046
9047 /* Copy dd::Column instant metadata */
9048
6/10
✓ Branch 0 taken 63 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 63 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 63 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 435 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 435 times.
✓ Branch 9 taken 63 times.
498 for (auto src_col : source->columns()) {
9049 dd::Column *dest_col =
9050
2/4
✓ Branch 0 taken 435 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 435 times.
✗ Branch 3 not taken.
435 const_cast<dd::Column *>(dd_find_column(dest, src_col->name().c_str()));
9051
9052
2/2
✓ Branch 0 taken 60 times.
✓ Branch 1 taken 375 times.
435 if (dest_col == nullptr) {
9053
1/2
✓ Branch 0 taken 60 times.
✗ Branch 1 not taken.
60 add_dropped_column(src_col);
9054
3/6
✓ Branch 0 taken 60 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 60 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 60 times.
60 ut_ad(nullptr != dd_find_column(dest, src_col->name().c_str()));
9055 60 continue;
9056 }
9057
9058
2/4
✓ Branch 0 taken 375 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 375 times.
375 if (dest_col->is_virtual()) {
9059 continue;
9060 }
9061
9062 2418 auto fn = [&](const char *s, auto &value) {
9063
4/6
✓ Branch 0 taken 1209 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1209 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 543 times.
✓ Branch 5 taken 666 times.
2418 if (src_col->se_private_data().exists(s)) {
9064
2/4
✓ Branch 0 taken 543 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 543 times.
✗ Branch 3 not taken.
1086 src_col->se_private_data().get(s, &value);
9065
2/4
✓ Branch 0 taken 543 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 543 times.
✗ Branch 3 not taken.
1086 dest_col->se_private_data().set(s, value);
9066 }
9067 2793 };
9068
9069 375 uint32_t v_added = UINT32_UNDEFINED;
9070 375 const char *s = dd_column_key_strings[DD_INSTANT_VERSION_ADDED];
9071
1/2
✓ Branch 0 taken 375 times.
✗ Branch 1 not taken.
375 fn(s, v_added);
9072
9073 375 uint32_t v_dropped = UINT32_UNDEFINED;
9074 375 s = dd_column_key_strings[DD_INSTANT_VERSION_DROPPED];
9075
1/2
✓ Branch 0 taken 375 times.
✗ Branch 1 not taken.
375 fn(s, v_dropped);
9076
9077 375 uint32_t phy_pos = UINT32_UNDEFINED;
9078 375 s = dd_column_key_strings[DD_INSTANT_PHYSICAL_POS];
9079
4/8
✓ Branch 0 taken 375 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 375 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 375 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 375 times.
375 ut_ad(src_col->se_private_data().exists(s));
9080
1/2
✓ Branch 0 taken 375 times.
✗ Branch 1 not taken.
375 fn(s, phy_pos);
9081
9082 375 s = dd_column_key_strings[DD_INSTANT_COLUMN_DEFAULT_NULL];
9083
5/8
✓ Branch 0 taken 375 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 375 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 375 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 24 times.
✓ Branch 7 taken 351 times.
375 if (src_col->se_private_data().exists(s)) {
9084
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 24 times.
24 ut_ad(v_added > 0);
9085 24 bool value = false;
9086
1/2
✓ Branch 0 taken 24 times.
✗ Branch 1 not taken.
24 fn(s, value);
9087 } else {
9088 351 s = dd_column_key_strings[DD_INSTANT_COLUMN_DEFAULT];
9089
5/8
✓ Branch 0 taken 351 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 351 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 351 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 60 times.
✓ Branch 7 taken 291 times.
351 if (src_col->se_private_data().exists(s)) {
9090
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 60 times.
60 ut_ad(v_added > 0);
9091 60 dd::String_type value;
9092
1/2
✓ Branch 0 taken 60 times.
✗ Branch 1 not taken.
60 fn(s, value);
9093 60 } else {
9094 /* This columns is not INSTANT ADD or this column is already dropped. */
9095
2/6
✗ Branch 0 not taken.
✓ Branch 1 taken 291 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 291 times.
291 ut_ad(v_added == UINT32_UNDEFINED || v_dropped > 0);
9096 }
9097 }
9098 }
9099 63 }
9100
9101 /** Class which handles the partition of states
9102 PART_TO_BE_DROPPED, PART_TO_BE_REORGED and PART_REORGED_DROPPED.
9103 See comments for alter_part_factory::create_for_reorg
9104 and alter_part_factory::create_for_non_reorg. */
9105 class alter_part_drop : public alter_part {
9106 public:
9107 /** Constructor
9108 @param[in] part_id Partition id in the table. This could
9109 be partition id for either old table
9110 or new table, callers should remember
9111 which one is applicable
9112 @param[in] state Partition state of the partition on
9113 which this class will do operations.
9114 If this is for one partition in new
9115 table, the partition state is the same
9116 for both the new partition and the
9117 corresponding old partition
9118 @param[in] table_name Partitioned table name, in the form
9119 of db/table, which already considers
9120 the charset
9121 @param[in,out] trx InnoDB transaction
9122 @param[in,out] old InnoDB table object for old partition,
9123 default is nullptr, which means there
9124 is no corresponding object
9125 @param[in] conflict True if there is already a partition
9126 table with the same name */
9127 1410 alter_part_drop(uint part_id, partition_state state, const char *table_name,
9128 trx_t *trx, dict_table_t **old, bool conflict)
9129 1410 : alter_part(trx, part_id, state, table_name, old),
9130 1410 m_conflict(conflict) {}
9131
9132 /** Destructor */
9133 2576 ~alter_part_drop() override = default;
9134
9135 /** Try to commit
9136 @param[in] table Table definition before the ALTER
9137 @param[in,out] altered_table Table definition after the ALTER
9138 @param[in] old_part the stored old partition or nullptr
9139 if no corresponding one exists
9140 @param[in,out] new_part the stored new partition or nullptr
9141 if no corresponding one exists
9142 @return 0 or error number */
9143 1332 int try_commit(const TABLE *table [[maybe_unused]],
9144 TABLE *altered_table [[maybe_unused]],
9145 const dd::Partition *old_part,
9146 dd::Partition *new_part) override {
9147
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1332 times.
1332 ut_ad(new_part == nullptr);
9148
9149
1/2
✓ Branch 0 taken 1332 times.
✗ Branch 1 not taken.
1332 dict_sys_mutex_enter();
9150
1/2
✓ Branch 0 taken 1332 times.
✗ Branch 1 not taken.
1332 dict_table_ddl_acquire(*m_old);
9151
1/2
✓ Branch 0 taken 1332 times.
✗ Branch 1 not taken.
1332 dict_sys_mutex_exit();
9152
1/2
✓ Branch 0 taken 1332 times.
✗ Branch 1 not taken.
1332 dd_table_close(*m_old, nullptr, nullptr, false);
9153
9154 int error;
9155 char part_name[FN_REFLEN];
9156 1332 THD *thd = m_trx->mysql_thd;
9157
9158
2/4
✓ Branch 0 taken 1332 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1332 times.
1332 if (!build_partition_name(old_part, false, part_name)) {
9159 return (HA_ERR_TOO_LONG_PATH); /* purecov: inspected */
9160 }
9161
9162
2/2
✓ Branch 0 taken 1262 times.
✓ Branch 1 taken 70 times.
1332 if (!m_conflict) {
9163
1/2
✓ Branch 0 taken 1252 times.
✗ Branch 1 not taken.
1262 error = innobase_basic_ddl::delete_impl<dd::Partition>(thd, part_name,
9164 old_part, nullptr);
9165
2/6
✓ Branch 0 taken 1252 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1252 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
1252 DBUG_EXECUTE_IF("drop_part_fail", error = DB_ERROR;
9166 my_error(ER_LOCK_WAIT_TIMEOUT, MYF(0)););
9167 } else {
9168 /* Have to rename it to a temporary name to prevent
9169 name conflict, because later deleting table doesn't
9170 remove the data file at once. Also notice that don't
9171 use the #tmp name, because it could be already used
9172 by the corresponding new partition. */
9173
1/2
✓ Branch 0 taken 70 times.
✗ Branch 1 not taken.
70 mem_heap_t *heap = mem_heap_create(FN_REFLEN, UT_LOCATION_HERE);
9174
9175 140 char *temp_name = dict_mem_create_temporary_tablename(
9176
1/2
✓ Branch 0 taken 70 times.
✗ Branch 1 not taken.
70 heap, (*m_old)->name.m_name, (*m_old)->id);
9177
9178 70 std::string db_str;
9179 70 std::string tbl_str;
9180
2/4
✓ Branch 0 taken 70 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 70 times.
✗ Branch 3 not taken.
70 dict_name::get_table(temp_name, db_str, tbl_str);
9181
9182 /* Acquire mdl lock on the temporary table name. */
9183 70 MDL_ticket *mdl_ticket = nullptr;
9184
2/4
✓ Branch 0 taken 70 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 70 times.
70 if (dd::acquire_exclusive_table_mdl(thd, db_str.c_str(), tbl_str.c_str(),
9185 false, &mdl_ticket)) {
9186 mem_heap_free(heap);
9187 return (HA_ERR_GENERIC);
9188 }
9189
9190
1/2
✓ Branch 0 taken 67 times.
✗ Branch 1 not taken.
70 error = innobase_basic_ddl::rename_impl<dd::Partition>(
9191 thd, part_name, temp_name, old_part, old_part, nullptr);
9192
1/2
✓ Branch 0 taken 67 times.
✗ Branch 1 not taken.
67 if (error == 0) {
9193
1/2
✓ Branch 0 taken 65 times.
✗ Branch 1 not taken.
67 error = innobase_basic_ddl::delete_impl<dd::Partition>(
9194 thd, temp_name, old_part, nullptr);
9195 }
9196
9197
1/2
✓ Branch 0 taken 65 times.
✗ Branch 1 not taken.
65 mem_heap_free(heap);
9198
2/4
✓ Branch 0 taken 65 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 65 times.
✗ Branch 3 not taken.
65 }
9199
9200
1/2
✓ Branch 0 taken 1317 times.
✗ Branch 1 not taken.
1317 free_old_part(error != 0, part_name);
9201
9202 1317 return (error);
9203 }
9204
9205 private:
9206 /** True if there is already a partition table with the same name */
9207 const bool m_conflict;
9208 };
9209
9210 /** Class which handles the partition of the state PART_CHANGED.
9211 See comments for alter_part_factory::create_for_reorg
9212 and alter_part_factory::create_for_non_reorg. */
9213 class alter_part_change : public alter_part {
9214 public:
9215 /** Constructor
9216 @param[in] part_id Partition id in the table. This could
9217 be partition id for either old table
9218 or new table, callers should remember
9219 which one is applicable
9220 @param[in] state Partition state of the partition on
9221 which this class will do operations.
9222 If this is for one partition in new
9223 table, the partition state is the same
9224 for both the new partition and the
9225 corresponding old partition
9226 @param[in] table_name Partitioned table name, in the form
9227 of db/table, which already considers
9228 the chraset
9229 @param[in] tablespace Tablespace specified explicitly
9230 @param[in,out] trx InnoDB transaction
9231 @param[in,out] old InnoDB table object for old partition,
9232 default is nullptr, which means there
9233 is no corresponding object
9234 @param[in] ha_alter_info ALTER information
9235 @param[in] file_per_table Current value of innodb_file_per_table
9236 @param[in] autoinc Next AUTOINC value to use */
9237 1069 alter_part_change(uint part_id, partition_state state, const char *table_name,
9238 const char *tablespace, trx_t *trx, dict_table_t **old,
9239 const Alter_inplace_info *ha_alter_info,
9240 bool file_per_table, uint64_t autoinc)
9241 1069 : alter_part(trx, part_id, state, table_name, old),
9242 1069 m_ha_alter_info(ha_alter_info),
9243 1069 m_file_per_table(file_per_table),
9244 1069 m_autoinc(autoinc) {
9245
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 1069 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
1069 if (tablespace == nullptr || tablespace[0] == '\0') {
9246 1069 m_tablespace[0] = '\0';
9247 } else {
9248 strcpy(m_tablespace, tablespace);
9249 }
9250 1069 }
9251
9252 /** Destructor */
9253 1778 ~alter_part_change() override = default;
9254
9255 /** Prepare
9256 @param[in,out] altered_table Table definition after the ALTER
9257 @param[in] old_part the stored old partition or nullptr
9258 if no corresponding one exists
9259 @param[in,out] new_part the stored new partition or nullptr
9260 if no corresponding one exists
9261 @return 0 or error number */
9262 int prepare(TABLE *altered_table, const dd::Partition *old_part,
9263 dd::Partition *new_part) override;
9264
9265 /** Try to commit
9266 @param[in] table Table definition before the ALTER
9267 @param[in,out] altered_table Table definition after the ALTER
9268 @param[in] old_part the stored old partition or nullptr
9269 if no corresponding one exists
9270 @param[in,out] new_part the stored new partition or nullptr
9271 if no corresponding one exists
9272 @return 0 or error number */
9273 int try_commit(const TABLE *table, TABLE *altered_table,
9274 const dd::Partition *old_part,
9275 dd::Partition *new_part) override;
9276
9277 /** Rollback */
9278 43 void rollback() override {
9279 /* Release the new table so that in post DDL, this table can be
9280 rolled back. */
9281
2/2
✓ Branch 0 taken 42 times.
✓ Branch 1 taken 1 times.
43 if (m_new != nullptr) {
9282 42 dd_table_close(m_new, m_trx->mysql_thd, nullptr, false);
9283 42 m_new = nullptr;
9284 }
9285 43 }
9286
9287 private:
9288 /** ALTER information */
9289 const Alter_inplace_info *m_ha_alter_info;
9290
9291 /** Current value of innodb_file_per_table */
9292 const bool m_file_per_table;
9293
9294 /** Next AUTOINC value to use */
9295 const uint64_t m_autoinc;
9296
9297 /** Tablespace of this partition */
9298 char m_tablespace[FN_REFLEN + 1];
9299 };
9300
9301 /** Prepare
9302 @param[in,out] altered_table Table definition after the ALTER
9303 @param[in] old_part the stored old partition or nullptr
9304 if no corresponding one exists
9305 @param[in,out] new_part the stored new partition or nullptr
9306 if no corresponding one exists
9307 @return 0 or error number */
9308 1051 int alter_part_change::prepare(TABLE *altered_table,
9309 const dd::Partition *old_part,
9310 dd::Partition *new_part) {
9311
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1051 times.
1051 ut_ad(old_part != nullptr);
9312
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1051 times.
1051 ut_ad(new_part != nullptr);
9313
9314 /* In some scenario, it could be unnecessary to create partition
9315 with temporary name, for example, old one is in innodb_system while
9316 new one is innodb_file_per_table. However, this would result in
9317 same table name for two tables, which is confusing. So the temporary
9318 name is used always and final rename is necessary too */
9319 char part_name[FN_REFLEN];
9320
9321
2/4
✓ Branch 0 taken 1051 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1051 times.
1051 if (!build_partition_name(new_part, true, part_name)) {
9322 return (HA_ERR_TOO_LONG_PATH); /* purecov: inspected */
9323 }
9324
9325 /* Copy the autoextend_size attribute for the partition being
9326 created. */
9327
1/2
✓ Branch 0 taken 1051 times.
✗ Branch 1 not taken.
1051 const dd::Table &part_table = old_part->table();
9328
9329 1051 ulonglong autoextend_size{};
9330
9331
2/4
✓ Branch 0 taken 1051 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1051 times.
✗ Branch 3 not taken.
1051 dd::get_implicit_tablespace_options(current_thd, &part_table,
9332 &autoextend_size);
9333
9334 int error =
9335
3/4
✓ Branch 0 taken 36 times.
✓ Branch 1 taken 1015 times.
✓ Branch 2 taken 1015 times.
✗ Branch 3 not taken.
1051 create(dd_table_has_instant_cols(part_table) ? &part_table : nullptr,
9336 1051 part_name, new_part, altered_table, m_tablespace, m_file_per_table,
9337
1/2
✓ Branch 0 taken 1051 times.
✗ Branch 1 not taken.
1051 m_autoinc, autoextend_size);
9338
9339
2/2
✓ Branch 0 taken 1014 times.
✓ Branch 1 taken 1 times.
1015 if (error == 0) {
9340
1/2
✓ Branch 0 taken 1014 times.
✗ Branch 1 not taken.
1014 dict_sys_mutex_enter();
9341
1/2
✓ Branch 0 taken 1014 times.
✗ Branch 1 not taken.
1014 m_new = dict_table_check_if_in_cache_low(part_name);
9342
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1014 times.
1014 ut_ad(m_new != nullptr);
9343
1/2
✓ Branch 0 taken 1014 times.
✗ Branch 1 not taken.
1014 m_new->acquire();
9344
1/2
✓ Branch 0 taken 1014 times.
✗ Branch 1 not taken.
1014 dict_table_ddl_release(m_new);
9345
1/2
✓ Branch 0 taken 1014 times.
✗ Branch 1 not taken.
1014 dict_sys_mutex_exit();
9346
9347 1014 return (m_new == nullptr);
9348 }
9349
9350 1 return (error);
9351 }
9352
9353 /** Try to commit
9354 @param[in] table Table definition before the ALTER
9355 @param[in,out] altered_table Table definition after the ALTER
9356 @param[in] old_part the stored old partition or nullptr
9357 if no corresponding one exists
9358 @param[in,out] new_part the stored new partition or nullptr
9359 if no corresponding one exists
9360 @return 0 or error number */
9361 894 int alter_part_change::try_commit(const TABLE *table [[maybe_unused]],
9362 TABLE *altered_table [[maybe_unused]],
9363 const dd::Partition *old_part,
9364 dd::Partition *new_part) {
9365
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 894 times.
894 ut_ad(old_part != nullptr);
9366
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 894 times.
894 ut_ad(new_part != nullptr);
9367
3/6
✓ Branch 0 taken 894 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 894 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 894 times.
894 ut_ad(old_part->name() == new_part->name());
9368
9369 894 THD *thd = m_trx->mysql_thd;
9370
9371 1788 char *temp_old_name = dict_mem_create_temporary_tablename(
9372
1/2
✓ Branch 0 taken 894 times.
✗ Branch 1 not taken.
894 (*m_old)->heap, (*m_old)->name.m_name, (*m_old)->id);
9373
9374
1/2
✓ Branch 0 taken 894 times.
✗ Branch 1 not taken.
894 dict_sys_mutex_enter();
9375
1/2
✓ Branch 0 taken 894 times.
✗ Branch 1 not taken.
894 dict_table_ddl_acquire(*m_old);
9376
1/2
✓ Branch 0 taken 894 times.
✗ Branch 1 not taken.
894 dict_sys_mutex_exit();
9377
1/2
✓ Branch 0 taken 894 times.
✗ Branch 1 not taken.
894 dd_table_close(*m_old, nullptr, nullptr, false);
9378
9379 894 std::string db_str;
9380 894 std::string tbl_str;
9381
2/4
✓ Branch 0 taken 894 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 894 times.
✗ Branch 3 not taken.
894 dict_name::get_table(temp_old_name, db_str, tbl_str);
9382
9383 /* Acquire mdl lock on the temporary table name. */
9384 894 MDL_ticket *mdl_ticket = nullptr;
9385
2/4
✓ Branch 0 taken 894 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 894 times.
894 if (dd::acquire_exclusive_table_mdl(thd, db_str.c_str(), tbl_str.c_str(),
9386 false, &mdl_ticket)) {
9387 return (HA_ERR_GENERIC);
9388 }
9389
9390 char old_name[FN_REFLEN];
9391 char temp_name[FN_REFLEN];
9392
9393
3/6
✓ Branch 0 taken 894 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 894 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 894 times.
1788 if (!build_partition_name(new_part, false, old_name) ||
9394
2/4
✓ Branch 0 taken 894 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 894 times.
894 !build_partition_name(new_part, true, temp_name)) {
9395 return (HA_ERR_TOO_LONG_PATH); /* purecov: inspected */
9396 }
9397
9398 int error;
9399
9400
1/2
✓ Branch 0 taken 882 times.
✗ Branch 1 not taken.
894 error = innobase_basic_ddl::rename_impl<dd::Partition>(
9401 thd, old_name, temp_old_name, old_part, old_part, nullptr);
9402
1/2
✓ Branch 0 taken 882 times.
✗ Branch 1 not taken.
882 if (error == 0) {
9403
1/2
✓ Branch 0 taken 870 times.
✗ Branch 1 not taken.
882 error = innobase_basic_ddl::rename_impl<dd::Partition>(
9404 thd, temp_name, old_name, new_part, new_part, nullptr);
9405
1/2
✓ Branch 0 taken 870 times.
✗ Branch 1 not taken.
870 if (error == 0) {
9406
1/2
✓ Branch 0 taken 862 times.
✗ Branch 1 not taken.
870 error = innobase_basic_ddl::delete_impl<dd::Partition>(thd, temp_old_name,
9407 old_part, nullptr);
9408
1/2
✓ Branch 0 taken 862 times.
✗ Branch 1 not taken.
862 free_old_part(error != 0, temp_old_name);
9409 }
9410 }
9411
9412
1/2
✓ Branch 0 taken 862 times.
✗ Branch 1 not taken.
862 if (m_new != nullptr) {
9413
1/2
✓ Branch 0 taken 862 times.
✗ Branch 1 not taken.
862 dd_table_close(m_new, thd, nullptr, false);
9414 862 m_new = nullptr;
9415 }
9416
9417 862 return (error);
9418 862 }
9419
9420 /** Create alter_part_* object(s) for subpartitions of a partition,
9421 or the partition itself
9422 @param[in,out] array Where to store the new object(s)
9423 @param[in] part partition_element to handle
9424 @param[in,out] part_id Partition id for both partition and
9425 subpartition, which would be increased
9426 by number of object(s) created
9427 @param[in] old_part_id Start partition id of the table before
9428 ALTER TABLE
9429 @param[in] state Partition state
9430 @param[in] conflict Only valid when state is
9431 PART_TO_BE_ADDED. True if the new
9432 (sub)partition has the same name with
9433 an exist one and they are of
9434 innodb_file_per_table
9435 @retval false On success
9436 @retval true On failure */
9437 4631 bool alter_part_factory::create_one(alter_part_array &array,
9438 partition_element *part, uint &part_id,
9439 uint old_part_id, partition_state state,
9440 bool conflict) {
9441
2/2
✓ Branch 0 taken 661 times.
✓ Branch 1 taken 3970 times.
4631 if (part->subpartitions.elements > 0) {
9442 partition_element *sub_elem;
9443
1/2
✓ Branch 0 taken 661 times.
✗ Branch 1 not taken.
661 List_iterator_fast<partition_element> new_sub_it(part->subpartitions);
9444
2/2
✓ Branch 0 taken 1643 times.
✓ Branch 1 taken 661 times.
2304 while ((sub_elem = new_sub_it++) != nullptr) {
9445 3286 const char *tablespace = partition_get_tablespace(
9446
1/2
✓ Branch 0 taken 1643 times.
✗ Branch 1 not taken.
1643 m_ha_alter_info->create_info->tablespace, part, sub_elem);
9447 alter_part *alter =
9448
1/2
✓ Branch 0 taken 1643 times.
✗ Branch 1 not taken.
1643 create_one_low(part_id, old_part_id++, state, tablespace, conflict);
9449
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1643 times.
1643 if (alter == nullptr) {
9450 return (true);
9451 }
9452
9453 1643 ++part_id;
9454
1/2
✓ Branch 0 taken 1643 times.
✗ Branch 1 not taken.
1643 array.push_back(alter);
9455 }
9456 } else {
9457 7940 const char *tablespace = partition_get_tablespace(
9458
1/2
✓ Branch 0 taken 3970 times.
✗ Branch 1 not taken.
3970 m_ha_alter_info->create_info->tablespace, part, nullptr);
9459 alter_part *alter =
9460
1/2
✓ Branch 0 taken 3970 times.
✗ Branch 1 not taken.
3970 create_one_low(part_id, old_part_id++, state, tablespace, conflict);
9461
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3970 times.
3970 if (alter == nullptr) {
9462 return (true);
9463 }
9464
9465 3970 ++part_id;
9466
1/2
✓ Branch 0 taken 3970 times.
✗ Branch 1 not taken.
3970 array.push_back(alter);
9467 }
9468
9469 4631 return (false);
9470 }
9471
9472 /** Create the specified alter_part_* object
9473 @param[in] part_id Partition id for current partition
9474
9475 @param[in] old_part_id Start partition id of the table before
9476 ALTER TABLE
9477 @param[in] state Partition state
9478 @param[in] tablespace Tablespace specified explicitly
9479 @param[in] conflict Only valid when state is
9480 PART_TO_BE_ADDED. True if the new
9481 (sub)partition has the same name with
9482 an exist one and they are of
9483 innodb_file_per_table
9484 @return alter_part_* object or nullptr */
9485 5613 alter_part *alter_part_factory::create_one_low(uint &part_id, uint old_part_id,
9486 partition_state state,
9487 const char *tablespace,
9488 bool conflict) {
9489 5613 alter_part *alter_part = nullptr;
9490
9491
4/5
✓ Branch 0 taken 2191 times.
✓ Branch 1 taken 943 times.
✓ Branch 2 taken 1410 times.
✓ Branch 3 taken 1069 times.
✗ Branch 4 not taken.
5613 switch (state) {
9492 2191 case PART_NORMAL:
9493
1/2
✓ Branch 0 taken 2191 times.
✗ Branch 1 not taken.
2191 alter_part = ut::new_withkey<alter_part_normal>(
9494 ut::make_psi_memory_key(mem_key_partitioning), part_id, state,
9495 2191 m_part_share->get_table_part_ref(old_part_id));
9496 2191 break;
9497 943 case PART_TO_BE_ADDED:
9498 943 alter_part = ut::new_withkey<alter_part_add>(
9499 ut::make_psi_memory_key(mem_key_partitioning), part_id, state,
9500 943 m_part_share->get_table_share()->normalized_path.str, tablespace,
9501 943 m_trx, m_ha_alter_info, m_file_per_table,
9502 943 m_part_share->next_auto_inc_val, conflict);
9503 943 break;
9504 1410 case PART_TO_BE_DROPPED:
9505 case PART_TO_BE_REORGED:
9506 case PART_REORGED_DROPPED:
9507
1/2
✓ Branch 0 taken 1410 times.
✗ Branch 1 not taken.
1410 alter_part = ut::new_withkey<alter_part_drop>(
9508 ut::make_psi_memory_key(mem_key_partitioning), part_id, state,
9509 1410 m_part_share->get_table_share()->normalized_path.str, m_trx,
9510 1410 m_part_share->get_table_part_ref(old_part_id), conflict);
9511 1410 break;
9512 1069 case PART_CHANGED:
9513
1/2
✓ Branch 0 taken 1069 times.
✗ Branch 1 not taken.
1069 alter_part = ut::new_withkey<alter_part_change>(
9514 ut::make_psi_memory_key(mem_key_partitioning), part_id, state,
9515 1069 m_part_share->get_table_share()->normalized_path.str, tablespace,
9516 1069 m_trx, m_part_share->get_table_part_ref(old_part_id), m_ha_alter_info,
9517 1069 m_file_per_table, m_part_share->next_auto_inc_val);
9518 1069 break;
9519 default:
9520 ut_d(ut_error);
9521 }
9522
9523 5613 return (alter_part);
9524 }
9525
9526 /** Create alter_part_add object(s) along with checking if the
9527 partition (and its subpartitions) conflicts with any of the original ones
9528 This is only for REORGANIZE PARTITION
9529 @param[in] new_part The new partition to check
9530 @param[in,out] new_part_id Partition id for both partition and
9531 subpartition, which would be increased
9532 by number of subpartitions per partition here
9533 @param[in,out] all_news To store the alter_part_add objects here
9534 @retval false On success
9535 @retval true On failure */
9536 341 bool alter_part_factory::create_new_checking_conflict(
9537 partition_element *new_part, uint &new_part_id,
9538 alter_part_array &all_news) {
9539
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 341 times.
341 ut_ad((m_ha_alter_info->handler_flags &
9540 Alter_inplace_info::REORGANIZE_PARTITION) != 0);
9541
9542 341 partition_info *part_info = m_ha_alter_info->modified_part_info;
9543 /* To compare with this partition list which contains all the
9544 to be reorganized partitions */
9545
1/2
✓ Branch 0 taken 341 times.
✗ Branch 1 not taken.
341 List_iterator_fast<partition_element> tmp_part_it(part_info->temp_partitions);
9546 partition_element *tmp_part_elem;
9547
9548
2/2
✓ Branch 0 taken 443 times.
✓ Branch 1 taken 257 times.
700 while ((tmp_part_elem = tmp_part_it++) != nullptr) {
9549
3/4
✓ Branch 0 taken 443 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 359 times.
✓ Branch 3 taken 84 times.
443 if (!is_conflict(new_part, tmp_part_elem)) {
9550 359 continue;
9551 }
9552
9553
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 76 times.
84 if (m_ha_alter_info->modified_part_info->is_sub_partitioned()) {
9554 List_iterator_fast<partition_element> tmp_sub_it(
9555
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 tmp_part_elem->subpartitions);
9556 partition_element *tmp_sub_elem;
9557
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 List_iterator_fast<partition_element> new_sub_it(new_part->subpartitions);
9558 partition_element *new_sub_elem;
9559
9560
2/2
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 8 times.
26 while ((new_sub_elem = new_sub_it++) != nullptr) {
9561
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 18 times.
18 ut_ad(new_sub_elem->partition_name != nullptr);
9562 18 tmp_sub_elem = tmp_sub_it++;
9563
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 18 times.
18 ut_ad(tmp_sub_elem != nullptr);
9564
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 18 times.
18 ut_ad(tmp_sub_elem->partition_name != nullptr);
9565
9566
1/2
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
18 bool conflict = is_conflict(new_sub_elem, tmp_sub_elem);
9567
2/4
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 18 times.
18 if (create_one(all_news, new_sub_elem, new_part_id, 0, PART_TO_BE_ADDED,
9568 conflict)) {
9569 return (true);
9570 }
9571 }
9572
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 ut_ad((tmp_sub_elem = tmp_sub_it++) == nullptr);
9573 } else {
9574
2/4
✓ Branch 0 taken 76 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 76 times.
76 if (create_one(all_news, new_part, new_part_id, 0, PART_TO_BE_ADDED,
9575 true)) {
9576 return (true);
9577 }
9578 }
9579
9580 /* Once matched, all are done */
9581 84 return (false);
9582 }
9583
9584 return (
9585
1/2
✓ Branch 0 taken 257 times.
✗ Branch 1 not taken.
257 create_one(all_news, new_part, new_part_id, 0, PART_TO_BE_ADDED, false));
9586 }
9587
9588 /** Create alter_part_drop object(s) along with checking if the
9589 partition (and its subpartitions) conflicts with any of the to
9590 be created ones.
9591 This is only for REORGANIZE PARTITION
9592 @param[in] old_part The old partition to check
9593 @param[in,out] old_part_id Partition id for this partition or
9594 the first subpartition, which would
9595 be increased by number of subpartitions
9596 per partition here
9597 @param[in,out] to_drop To store the alter_part_drop objects
9598 @retval false On success
9599 @retval true On failure */
9600 369 bool alter_part_factory::create_old_checking_conflict(
9601 partition_element *old_part, uint &old_part_id, alter_part_array &to_drop) {
9602
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 369 times.
369 ut_ad((m_ha_alter_info->handler_flags &
9603 Alter_inplace_info::REORGANIZE_PARTITION) != 0);
9604
9605 369 partition_info *part_info = m_ha_alter_info->modified_part_info;
9606 /* To compare with this partition list which contains all the
9607 new to be added partitions */
9608
1/2
✓ Branch 0 taken 369 times.
✗ Branch 1 not taken.
369 List_iterator_fast<partition_element> part_it(part_info->partitions);
9609 partition_element *part_elem;
9610
9611
2/2
✓ Branch 0 taken 879 times.
✓ Branch 1 taken 285 times.
1164 while ((part_elem = part_it++) != nullptr) {
9612
3/4
✓ Branch 0 taken 879 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 795 times.
✓ Branch 3 taken 84 times.
879 if (!is_conflict(part_elem, old_part)) {
9613 795 continue;
9614 }
9615
9616
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 76 times.
84 if (m_ha_alter_info->modified_part_info->is_sub_partitioned()) {
9617
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 List_iterator_fast<partition_element> sub_it(part_elem->subpartitions);
9618 partition_element *sub_elem;
9619
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 List_iterator_fast<partition_element> old_sub_it(old_part->subpartitions);
9620 partition_element *old_sub_elem;
9621
9622
2/2
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 8 times.
26 while ((old_sub_elem = old_sub_it++) != nullptr) {
9623
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 18 times.
18 ut_ad(old_sub_elem->partition_name != nullptr);
9624 18 sub_elem = sub_it++;
9625
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 18 times.
18 ut_ad(sub_elem != nullptr);
9626
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 18 times.
18 ut_ad(sub_elem->partition_name != nullptr);
9627
9628
1/2
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
18 bool conflict = is_conflict(sub_elem, old_sub_elem);
9629
2/4
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 18 times.
18 if (create_one(to_drop, old_sub_elem, old_part_id, old_part_id,
9630 PART_TO_BE_REORGED, conflict)) {
9631 return (true);
9632 }
9633 }
9634
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 ut_ad((sub_elem = sub_it++) == nullptr);
9635 } else {
9636
2/4
✓ Branch 0 taken 76 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 76 times.
76 if (create_one(to_drop, old_part, old_part_id, old_part_id,
9637 PART_TO_BE_REORGED, true)) {
9638 return (true);
9639 }
9640 }
9641
9642 /* Once matched, all are done */
9643 84 return (false);
9644 }
9645
9646
1/2
✓ Branch 0 taken 285 times.
✗ Branch 1 not taken.
285 return (create_one(to_drop, old_part, old_part_id, old_part_id,
9647 285 PART_TO_BE_REORGED, false));
9648 }
9649
9650 /** Check if the two (sub)partitions conflict with each other,
9651 Which means they have same name.
9652 @param[in] new_part New partition to check
9653 @param[in] old_part Old partition to check
9654 @retval true Conflict
9655 @retval false Not conflict */
9656 1358 bool alter_part_factory::is_conflict(const partition_element *new_part,
9657 const partition_element *old_part) {
9658 1358 if (my_strcasecmp(system_charset_info, new_part->partition_name,
9659
2/2
✓ Branch 0 taken 1166 times.
✓ Branch 1 taken 192 times.
1358 old_part->partition_name) != 0) {
9660 1166 return (false);
9661 }
9662
9663 /* To prevent the conflict(same) names in table cache, not to
9664 check the innodb_file_per_table */
9665 192 return (true);
9666 }
9667
9668 /** Suppose that there is a table with 4 range partitions: p0, p1, p2, p3,
9669 and the p2 and p3 are going to be reorganized into p21, p22, p31, p33.
9670
9671 In modified_part_info->temp_partitions list, there are only p2 and p3
9672 with the state PART_TO_BE_REORGED, while in modified_part_info->partitions
9673 list, it contains
9674 {PART_NORMAL, PART_NORMAL, PART_TO_BE_ADDED, PART_TO_BE_ADDED,
9675 PART_TO_BE_ADDED, PART_TO_BE_ADDED}.
9676
9677 So finally, the to_drop array would contain
9678 {alter_part_drop, alter_part_drop}, which are for p2, p3;
9679 the all_news array would contains
9680 {alter_part_normal, alter_part_normal, alter_part_add, alter_part_add,
9681 alter_part_add, alter_part_add}.
9682
9683 Note that the scenario that reorganized and to be reorganized
9684 partition/subpartition have the same name, would be checked here too */
9685
9686 /** Create the alter_part_* objects when it's an operation like
9687 REORGANIZE PARTITION
9688 @param[in,out] to_drop To store the alter_part_* objects
9689 for partitions to be dropped
9690 @param[in,out] all_news To store the alter_part_* objects
9691 for partitions in table after ALTER TABLE
9692 @return false On success
9693 @retval true On failure */
9694 231 bool alter_part_factory::create_for_reorg(alter_part_array &to_drop,
9695 alter_part_array &all_news) {
9696
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 231 times.
231 ut_ad((m_ha_alter_info->handler_flags &
9697 Alter_inplace_info::REORGANIZE_PARTITION) != 0);
9698
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 231 times.
231 ut_ad(m_ha_alter_info->modified_part_info->num_subparts ==
9699 m_old_part_info->num_subparts);
9700
9701 231 partition_info *part_info = m_ha_alter_info->modified_part_info;
9702 /* This list contains only the to be reorganized partitions,
9703 the sequence is the same as the list of m_old_part_info,
9704 and they should be consecutive ones */
9705
1/2
✓ Branch 0 taken 231 times.
✗ Branch 1 not taken.
231 List_iterator_fast<partition_element> tmp_part_it(part_info->temp_partitions);
9706 /* This list contains all the new partitions */
9707
1/2
✓ Branch 0 taken 231 times.
✗ Branch 1 not taken.
231 List_iterator_fast<partition_element> part_it(part_info->partitions);
9708 /* This list contains all the old partitions */
9709 List_iterator_fast<partition_element> old_part_it(
9710
1/2
✓ Branch 0 taken 231 times.
✗ Branch 1 not taken.
231 m_old_part_info->partitions);
9711 partition_element *part_elem;
9712 partition_element *tmp_part_elem;
9713 partition_element *old_part_elem;
9714 uint parts_per_part =
9715
2/2
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 216 times.
231 part_info->is_sub_partitioned() ? part_info->num_subparts : 1;
9716
9717 231 tmp_part_elem = tmp_part_it++;
9718
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 231 times.
231 ut_ad(tmp_part_elem != nullptr);
9719 231 old_part_elem = old_part_it++;
9720
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 231 times.
231 ut_ad(old_part_elem != nullptr);
9721
9722 231 uint old_part_id = 0;
9723 231 uint new_part_id = 0;
9724
9725 /* There are 3 steps here:
9726 1. Check if the old one is a to be reorganized one, if so, mark it
9727 and check next old one
9728 2. If not, check if the new one is a to be added one, if so, mark it
9729 and check next new one
9730 3. If not, the old one and the new one should point to the same
9731 partition */
9732
2/2
✓ Branch 0 taken 593 times.
✓ Branch 1 taken 231 times.
824 while ((part_elem = part_it++) != nullptr) {
9733
4/4
✓ Branch 0 taken 700 times.
✓ Branch 1 taken 262 times.
✓ Branch 2 taken 529 times.
✓ Branch 3 taken 171 times.
962 while (old_part_elem != nullptr && tmp_part_elem != nullptr &&
9734
2/2
✓ Branch 0 taken 369 times.
✓ Branch 1 taken 160 times.
529 strcmp(tmp_part_elem->partition_name,
9735 old_part_elem->partition_name) == 0) {
9736
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 369 times.
369 ut_ad(tmp_part_elem->part_state == PART_TO_BE_REORGED);
9737
9738
2/4
✓ Branch 0 taken 369 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 369 times.
369 if (create_old_checking_conflict(old_part_elem, old_part_id, to_drop)) {
9739 return (true);
9740 }
9741
9742 369 old_part_elem = old_part_it++;
9743 369 tmp_part_elem = tmp_part_it++;
9744 }
9745
9746
2/3
✓ Branch 0 taken 341 times.
✓ Branch 1 taken 252 times.
✗ Branch 2 not taken.
593 switch (part_elem->part_state) {
9747 341 case PART_TO_BE_ADDED:
9748
9749
2/4
✓ Branch 0 taken 341 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 341 times.
341 if (create_new_checking_conflict(part_elem, new_part_id, all_news)) {
9750 return (true);
9751 }
9752
9753 341 break;
9754
9755 252 case PART_NORMAL:
9756
9757
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 252 times.
252 ut_ad(strcmp(part_elem->partition_name,
9758 old_part_elem->partition_name) == 0);
9759
9760
2/4
✓ Branch 0 taken 252 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 252 times.
252 if (create_one(all_news, part_elem, new_part_id, old_part_id,
9761 PART_NORMAL, false)) {
9762 return (true);
9763 }
9764
9765 252 old_part_elem = old_part_it++;
9766 252 old_part_id += parts_per_part;
9767
9768 252 break;
9769
9770 default:
9771 ut_d(ut_error);
9772 }
9773 }
9774
9775
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 231 times.
231 ut_ad(old_part_elem == nullptr);
9776
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 231 times.
231 ut_ad(tmp_part_elem == nullptr);
9777
9778 231 return (false);
9779 }
9780
9781 /** Suppose that there is a table with 4 range partitions: p0, p1, p2, p3.
9782
9783 1. ADD PARTITION p4
9784 modified_part_info->partitions list contains
9785 {PART_NORMAL, PART_NORMAL, PART_NORMAL, PART_NORMAL, PART_TO_BE_ADDED}.
9786
9787 So finally, the to_drop array would contain
9788 {}, which is empty;
9789 the all_news array would contains
9790 {alter_part_normal, alter_part_normal, alter_part_normal, alter_part_normal,
9791 alter_part_add}.
9792
9793 2. DROP PARTITION p2
9794 modified_part_info->partitions list contain
9795 {PART_NORMAL, PART_NORMAL, PART_TO_BE_DROPPED, PART_NORMAL}.
9796
9797 So finally, the to_drop array would contain
9798 {alter_part_drop}, which is for p2, so part_id is 2;
9799 the all_news array would contains
9800 {alter_part_normal, alter_part_normal, alter_part_normal}.
9801
9802
9803 Suppose it's the same table with 4 partitions, but it's partitioned by HASH.
9804
9805 3. ADD PARTITION 2
9806 modified_part_info->partitions list contains
9807 {PART_CHANGED, PART_CHANGED, PART_CHANGED, PART_CHANGED, PART_TO_BE_ADDED,
9808 PART_TO_BE_ADDED}.
9809
9810 So finally, the to_drop array would contain
9811 {}, which is empty;
9812 the all_news array would contains
9813 {alter_part_change, alter_part_change, alter_part_change, alter_part_change,
9814 alter_part_add, alter_part_add}.
9815
9816 4. COALESCE PARTITION 2
9817 modified_part_info->partitions contains:
9818 {PART_CHANGED, PART_CHANGED, PART_REORGED_DROPPED, PART_REORGED_DROPPED}.
9819
9820 So finally, the to_drop array would contain
9821 {alter_part_drop, alter_part_drop}, which are for p2, p3, part_id are 2 and 3;
9822 the all_news array would contains
9823 {alter_part_change, alter_part_change}.
9824
9825 5. REBUILD PARTITION p0, p2
9826 modified_part_info->partitions contains:
9827 {PART_NORMAL, PART_CHANGED, PART_NORMAL, PART_CHANGED}.
9828
9829 So finally, the to_drop array would contain
9830 {}, which is empty;
9831 the all_news array would contains
9832 {alter_part_normal, alter_part_change, alter_part_normal, alter_part_change}. */
9833
9834 /** Create the alter_part_* objects when it's NOT an operation like
9835 REORGANIZE PARTITION
9836 @param[in,out] to_drop To store the alter_part_* objects
9837 for partitions to be dropped
9838 @param[in,out] all_news To store the alter_part_* objects
9839 for partitions in table after ALTER TABLE
9840 @return false On success
9841 @retval true On Failure */
9842 769 bool alter_part_factory::create_for_non_reorg(alter_part_array &to_drop,
9843 alter_part_array &all_news) {
9844
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 769 times.
769 ut_ad((m_ha_alter_info->handler_flags &
9845 Alter_inplace_info::REORGANIZE_PARTITION) == 0);
9846
9847 769 partition_info *part_info = m_ha_alter_info->modified_part_info;
9848 uint parts_per_part =
9849
2/2
✓ Branch 0 taken 173 times.
✓ Branch 1 taken 596 times.
769 part_info->is_sub_partitioned() ? part_info->num_subparts : 1;
9850
1/2
✓ Branch 0 taken 769 times.
✗ Branch 1 not taken.
769 List_iterator_fast<partition_element> part_it(part_info->partitions);
9851 partition_element *part_elem;
9852 769 uint old_part_id = 0;
9853 769 uint new_part_id = 0;
9854
9855
2/2
✓ Branch 0 taken 3649 times.
✓ Branch 1 taken 769 times.
4418 while ((part_elem = part_it++) != nullptr) {
9856 3649 partition_state state = part_elem->part_state;
9857
3/4
✓ Branch 0 taken 2211 times.
✓ Branch 1 taken 557 times.
✓ Branch 2 taken 881 times.
✗ Branch 3 not taken.
3649 switch (state) {
9858 2211 case PART_NORMAL:
9859 case PART_CHANGED:
9860
2/4
✓ Branch 0 taken 2211 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 2211 times.
2211 if (create_one(all_news, part_elem, new_part_id, old_part_id, state,
9861 false)) {
9862 return (true);
9863 }
9864
9865 2211 old_part_id += parts_per_part;
9866 2211 break;
9867 557 case PART_TO_BE_ADDED:
9868
2/4
✓ Branch 0 taken 557 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 557 times.
557 if (create_one(all_news, part_elem, new_part_id, 0, state, false)) {
9869 return (true);
9870 }
9871
9872 557 break;
9873 881 case PART_TO_BE_DROPPED:
9874 case PART_REORGED_DROPPED:
9875
2/4
✓ Branch 0 taken 881 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 881 times.
881 if (create_one(to_drop, part_elem, old_part_id, old_part_id, state,
9876 false)) {
9877 return (true);
9878 }
9879
9880 881 break;
9881 default:
9882 ut_d(ut_error);
9883 }
9884 }
9885
9886 769 return (false);
9887 }
9888
9889 #ifndef NDEBUG
9890 /** Check if the specified partition_state is of drop state
9891 @param[in] s The state to be checked
9892 @retval true if this is of a drop state
9893 @retval false if not */
9894 834 inline static bool is_drop_state(partition_state s) {
9895
4/6
✓ Branch 0 taken 140 times.
✓ Branch 1 taken 694 times.
✓ Branch 2 taken 140 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 140 times.
✗ Branch 5 not taken.
834 return (s == PART_TO_BE_DROPPED || s == PART_REORGED_DROPPED ||
9896 834 s == PART_TO_BE_REORGED);
9897 }
9898 #endif
9899
9900 /** Check if the specified partition_state is of common state
9901 @param[in] s The state to be checked
9902 @retval true if this is of a common state
9903 @retval false if not */
9904 9548 inline static bool is_common_state(partition_state s) {
9905
4/4
✓ Branch 0 taken 5389 times.
✓ Branch 1 taken 4159 times.
✓ Branch 2 taken 1945 times.
✓ Branch 3 taken 3444 times.
9548 return (s == PART_NORMAL || s == PART_CHANGED);
9906 }
9907
9908 /** Destructor */
9909 1750 alter_parts::~alter_parts() {
9910
2/2
✓ Branch 0 taken 3806 times.
✓ Branch 1 taken 875 times.
9362 for (alter_part *alter_part : m_news) {
9911 7612 ut::delete_(alter_part);
9912 }
9913
9914
2/2
✓ Branch 0 taken 1288 times.
✓ Branch 1 taken 875 times.
4326 for (alter_part *alter_part : m_to_drop) {
9915 2576 ut::delete_(alter_part);
9916 }
9917 }
9918
9919 /** Create the to be created partitions and update internal
9920 structures with concurrent writes blocked, while preparing
9921 ALTER TABLE.
9922 @param[in] old_dd_tab dd::Table before ALTER TABLE
9923 @param[in,out] new_dd_tab dd::Table after ALTER TABLE
9924 @param[in,out] altered_table Table definition after the ALTER
9925 @return 0 or error number, my_error() should be called by callers */
9926 1000 int alter_parts::prepare(const dd::Table &old_dd_tab, dd::Table &new_dd_tab,
9927 TABLE *altered_table) {
9928
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1000 times.
1000 if (m_factory.create(m_to_drop, m_news)) {
9929 return (true);
9930 }
9931
9932
2/2
✓ Branch 0 taken 246 times.
✓ Branch 1 taken 754 times.
1000 if (m_part_share->get_table_share()->found_next_number_field) {
9933 246 dd_set_autoinc(new_dd_tab.se_private_data(),
9934 246 m_ha_alter_info->create_info->auto_increment_value);
9935 }
9936
9937 int error;
9938 1000 error = prepare_or_commit_for_old(old_dd_tab, altered_table, true);
9939
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1000 times.
1000 if (error != 0) {
9940 return (error);
9941 }
9942
9943 error =
9944 1000 prepare_or_commit_for_new(old_dd_tab, new_dd_tab, altered_table, true);
9945
9946 /* We don't have to prepare for the partitions that will be dropped. */
9947
9948 925 return (error);
9949 }
9950
9951 /** Notify the storage engine that the changes made during
9952 prepare_inplace_alter_table() and inplace_alter_table()
9953 will be rolled back for all the partitions. */
9954 42 void alter_parts::rollback() {
9955
2/2
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 42 times.
57 for (alter_part *alter_part : m_to_drop) {
9956
1/2
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
15 alter_part->rollback();
9957 }
9958
9959
2/2
✓ Branch 0 taken 233 times.
✓ Branch 1 taken 42 times.
275 for (alter_part *alter_part : m_news) {
9960
1/2
✓ Branch 0 taken 233 times.
✗ Branch 1 not taken.
233 alter_part->rollback();
9961 }
9962 42 }
9963
9964 /** Try to commit the changes made during prepare_inplace_alter_table()
9965 inside the storage engine. This is protected by MDL_EXCLUSIVE.
9966 @param[in] old_dd_tab dd::Table before ALTER TABLE
9967 @param[in,out] new_dd_tab dd::Table after ALTER TABLE
9968 @param[in] table Table definition before the ALTER
9969 @param[in,out] altered_table Table definition after the ALTER
9970 @return 0 or error number, my_error() should be called by callers */
9971 883 int alter_parts::try_commit(const dd::Table &old_dd_tab, dd::Table &new_dd_tab,
9972 const TABLE *table [[maybe_unused]],
9973 TABLE *altered_table) {
9974 int error;
9975 /* Commit for the old ones first, to clear data files for new ones */
9976 883 error = prepare_or_commit_for_old(old_dd_tab, altered_table, false);
9977
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 868 times.
868 if (error != 0) {
9978 return (error);
9979 }
9980
9981 error =
9982 868 prepare_or_commit_for_new(old_dd_tab, new_dd_tab, altered_table, false);
9983
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 833 times.
833 if (error != 0) {
9984 return (error);
9985 }
9986
9987 833 return (0);
9988 }
9989
9990 /** Prepare for all the partitions in table after ALTER TABLE
9991 @param[in] old_dd_tab dd::Table before ALTER TABLE
9992 @param[in,out] new_dd_tab dd::Table after ALTER TABLE
9993 @param[in,out] altered_table Table definition after the ALTER
9994 @param[in] prepare true if it's in prepare phase,
9995 false if it's in commit phase
9996 @return 0 or error number */
9997 1868 int alter_parts::prepare_or_commit_for_new(const dd::Table &old_dd_tab,
9998 dd::Table &new_dd_tab,
9999 TABLE *altered_table, bool prepare) {
10000
1/2
✓ Branch 0 taken 1868 times.
✗ Branch 1 not taken.
1868 auto oldp = old_dd_tab.leaf_partitions().begin();
10001 1868 uint new_part_id = 0;
10002 1868 uint old_part_id = 0;
10003 1868 uint drop_seq = 0;
10004 1868 const dd::Partition *old_part = nullptr;
10005 1868 int error = 0;
10006
10007
3/4
✓ Branch 0 taken 1868 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 7761 times.
✓ Branch 3 taken 1745 times.
9506 for (auto new_part : *new_dd_tab.leaf_partitions()) {
10008
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 7761 times.
7761 ut_ad(new_part_id < m_news.size());
10009
10010 /* To add a new partition, there is no corresponding old one,
10011 otherwise, find the old one */
10012
1/2
✓ Branch 0 taken 7761 times.
✗ Branch 1 not taken.
7761 partition_state s = m_news[new_part_id]->state();
10013
2/2
✓ Branch 0 taken 6104 times.
✓ Branch 1 taken 1657 times.
7761 if (is_common_state(s)) {
10014 6104 bool found = false;
10015
7/8
✓ Branch 0 taken 13042 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 11953 times.
✓ Branch 3 taken 1089 times.
✓ Branch 4 taken 6938 times.
✓ Branch 5 taken 5015 times.
✓ Branch 6 taken 6938 times.
✓ Branch 7 taken 6104 times.
13042 for (; oldp != old_dd_tab.leaf_partitions().end() && !found; ++oldp) {
10016 6938 old_part = *oldp;
10017
10018 6938 ++old_part_id;
10019
4/4
✓ Branch 0 taken 2268 times.
✓ Branch 1 taken 4670 times.
✓ Branch 2 taken 834 times.
✓ Branch 3 taken 6104 times.
9206 if (drop_seq < m_to_drop.size() &&
10020
3/4
✓ Branch 0 taken 2268 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 834 times.
✓ Branch 3 taken 1434 times.
2268 (old_part_id - 1 == m_to_drop[drop_seq]->part_id())) {
10021
2/4
✓ Branch 0 taken 834 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 834 times.
834 ut_ad(is_drop_state(m_to_drop[drop_seq]->state()));
10022 834 ++drop_seq;
10023 834 continue;
10024 }
10025
10026 6104 found = true;
10027 }
10028
10029
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6104 times.
6104 ut_ad(found);
10030
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6104 times.
6104 ut_ad(drop_seq <= m_to_drop.size());
10031
3/6
✓ Branch 0 taken 6104 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 6104 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 6104 times.
6104 ut_ad(new_part->name() == old_part->name());
10032
3/6
✓ Branch 0 taken 6104 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 6104 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 6104 times.
6104 ut_ad((new_part->parent() == nullptr) == (old_part->parent() == nullptr));
10033
9/16
✓ Branch 0 taken 6104 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2492 times.
✓ Branch 3 taken 3612 times.
✓ Branch 4 taken 2492 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 2492 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 2492 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 2492 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 2492 times.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✓ Branch 15 taken 6104 times.
6104 ut_ad(new_part->parent() == nullptr ||
10034 new_part->parent()->name() == old_part->parent()->name());
10035 } else {
10036
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1657 times.
1657 ut_ad(s == PART_TO_BE_ADDED);
10037 /* Let's still set one to get the old table name */
10038
1/2
✓ Branch 0 taken 1657 times.
✗ Branch 1 not taken.
1657 old_part = *(old_dd_tab.leaf_partitions().begin());
10039 }
10040
10041 7761 alter_part *alter_part = m_news[new_part_id];
10042
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 7761 times.
7761 ut_ad(alter_part != nullptr);
10043
10044
2/2
✓ Branch 0 taken 4131 times.
✓ Branch 1 taken 3630 times.
7761 if (prepare) {
10045
1/2
✓ Branch 0 taken 4056 times.
✗ Branch 1 not taken.
4131 error = alter_part->prepare(altered_table, old_part, new_part);
10046
2/2
✓ Branch 0 taken 13 times.
✓ Branch 1 taken 4043 times.
4056 if (error != 0) {
10047 13 return (error);
10048 }
10049
10050
6/6
✓ Branch 0 taken 2484 times.
✓ Branch 1 taken 1559 times.
✓ Branch 2 taken 1733 times.
✓ Branch 3 taken 751 times.
✓ Branch 4 taken 1733 times.
✓ Branch 5 taken 2310 times.
4043 if (m_new_partitions != nullptr && alter_part->new_table() != nullptr) {
10051
1/2
✓ Branch 0 taken 1733 times.
✗ Branch 1 not taken.
1733 m_new_partitions->set_part(new_part_id, alter_part->new_table());
10052 }
10053 } else {
10054 error =
10055
1/2
✓ Branch 0 taken 3595 times.
✗ Branch 1 not taken.
3630 alter_part->try_commit(nullptr, altered_table, old_part, new_part);
10056
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3595 times.
3595 if (error != 0) {
10057 return (error);
10058 }
10059 }
10060
10061 7638 ++new_part_id;
10062 }
10063
10064 #ifdef UNIV_DEBUG
10065
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1745 times.
1745 ut_ad(drop_seq <= m_to_drop.size());
10066
2/2
✓ Branch 0 taken 1787 times.
✓ Branch 1 taken 1745 times.
3532 for (uint i = drop_seq; i < m_to_drop.size(); ++i) {
10067
2/4
✓ Branch 0 taken 1787 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1787 times.
1787 ut_ad(!is_common_state(m_to_drop[i]->state()));
10068 }
10069 #endif /* UNIV_DEBUG */
10070
10071 1745 return (error);
10072 }
10073
10074 /** Prepare or commit for all the partitions in table before ALTER TABLE
10075 @param[in] old_dd_tab dd::Table before ALTER TABLE
10076 @param[in,out] altered_table Table definition after the ALTER
10077 @param[in] prepare true if it's in prepare phase,
10078 false if it's in commit phase
10079 @return 0 or error number */
10080 1883 int alter_parts::prepare_or_commit_for_old(const dd::Table &old_dd_tab,
10081 TABLE *altered_table, bool prepare) {
10082 1883 uint old_part_id = 0;
10083
1/2
✓ Branch 0 taken 1883 times.
✗ Branch 1 not taken.
1883 auto dd_part = old_dd_tab.leaf_partitions().begin();
10084 1883 int error = 0;
10085
10086
2/2
✓ Branch 0 taken 2742 times.
✓ Branch 1 taken 1868 times.
4610 for (alter_part *alter_part : m_to_drop) {
10087 2742 const dd::Partition *old_part = nullptr;
10088
10089
2/4
✓ Branch 0 taken 4221 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4221 times.
✗ Branch 3 not taken.
4221 for (; dd_part != old_dd_tab.leaf_partitions().end(); ++dd_part) {
10090
3/4
✓ Branch 0 taken 4221 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1479 times.
✓ Branch 3 taken 2742 times.
4221 if (old_part_id++ < alter_part->part_id()) {
10091 1479 continue;
10092 }
10093
10094 2742 old_part = *dd_part;
10095 2742 ++dd_part;
10096 2742 break;
10097 }
10098
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2742 times.
2742 ut_ad(old_part != nullptr);
10099
10100
2/2
✓ Branch 0 taken 1410 times.
✓ Branch 1 taken 1332 times.
2742 if (prepare) {
10101
1/2
✓ Branch 0 taken 1410 times.
✗ Branch 1 not taken.
1410 error = alter_part->prepare(altered_table, old_part, nullptr);
10102 } else {
10103
1/2
✓ Branch 0 taken 1317 times.
✗ Branch 1 not taken.
1332 error = alter_part->try_commit(nullptr, altered_table, old_part, nullptr);
10104 }
10105
10106
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2727 times.
2727 if (error != 0) {
10107 return (error);
10108 }
10109 }
10110
10111 1868 return (error);
10112 }
10113
10114 /** Determine if one ALTER TABLE can be done instantly on the partitioned table
10115 @param[in] ha_alter_info the DDL operation
10116 @param[in] num_parts number of partitions
10117 @param[in] part_share the partitioned tables
10118 @param[in] old_table old TABLE
10119 @param[in] altered_table new TABLE
10120 @return Instant_Type accordingly */
10121 1882 static inline Instant_Type innopart_support_instant(
10122 const Alter_inplace_info *ha_alter_info, uint16_t num_parts,
10123 const Ha_innopart_share *part_share, const TABLE *old_table,
10124 const TABLE *altered_table) {
10125 1882 Instant_Type type = Instant_Type::INSTANT_IMPOSSIBLE;
10126
10127
2/2
✓ Branch 0 taken 2818 times.
✓ Branch 1 taken 281 times.
3099 for (uint32_t i = 0; i < num_parts; ++i) {
10128 5636 type = innobase_support_instant(
10129 2818 ha_alter_info, part_share->get_table_part(i), old_table, altered_table);
10130
2/2
✓ Branch 0 taken 1601 times.
✓ Branch 1 taken 1217 times.
2818 if (type == Instant_Type::INSTANT_IMPOSSIBLE) {
10131 1601 return (type);
10132 }
10133 }
10134
10135 281 return (type);
10136 }
10137
10138 int ha_innopart::parallel_scan_init(void *&scan_ctx, size_t *num_threads,
10139 bool use_reserved_threads) {
10140 auto max_threads = thd_parallel_read_threads(m_prebuilt->trx->mysql_thd);
10141 ut_a(max_threads <= Parallel_reader::MAX_THREADS);
10142
10143 max_threads = static_cast<ulong>(
10144 Parallel_reader::available_threads(max_threads, use_reserved_threads));
10145
10146 if (max_threads == 0) {
10147 return (HA_ERR_GENERIC);
10148 }
10149
10150 scan_ctx = nullptr;
10151
10152 const auto row_len = m_prebuilt->mysql_row_len;
10153
10154 auto adapter = ut::new_withkey<Parallel_reader_adapter>(
10155 UT_NEW_THIS_FILE_PSI_KEY, max_threads, row_len);
10156
10157 if (adapter == nullptr) {
10158 Parallel_reader::release_threads(max_threads);
10159 return (HA_ERR_OUT_OF_MEM);
10160 }
10161
10162 auto trx = m_prebuilt->trx;
10163
10164 innobase_register_trx(ht, ha_thd(), trx);
10165
10166 trx_start_if_not_started_xa(trx, false, UT_LOCATION_HERE);
10167
10168 trx_assign_read_view(trx);
10169
10170 const Parallel_reader::Scan_range FULL_SCAN{};
10171 const auto first_used_partition = m_part_info->get_first_used_partition();
10172
10173 for (auto i = first_used_partition; i < m_tot_parts;
10174 i = m_part_info->get_next_used_partition(i)) {
10175 set_partition(i);
10176
10177 if (dict_table_is_discarded(m_prebuilt->table)) {
10178 ib_senderrf(ha_thd(), IB_LOG_LEVEL_ERROR, ER_TABLESPACE_DISCARDED,
10179 m_prebuilt->table->name.m_name);
10180
10181 ut::delete_(adapter);
10182 return HA_ERR_NO_SUCH_TABLE;
10183 }
10184
10185 build_template(true);
10186
10187 Parallel_reader::Config config(FULL_SCAN, m_prebuilt->table->first_index(),
10188 0, i);
10189
10190 dberr_t err =
10191 adapter->add_scan(trx, config, [=](const Parallel_reader::Ctx *ctx) {
10192 return (adapter->process_rows(ctx));
10193 });
10194
10195 if (err != DB_SUCCESS) {
10196 ut::delete_(adapter);
10197 return (convert_error_code_to_mysql(err, 0, ha_thd()));
10198 }
10199 }
10200
10201 scan_ctx = adapter;
10202 *num_threads = max_threads;
10203
10204 adapter->set(m_prebuilt);
10205
10206 return (0);
10207 }
10208
10209 int ha_innopart::parallel_scan(void *scan_ctx, void **thread_ctxs,
10210 Parallel_reader_adapter::Init_fn init_fn,
10211 Parallel_reader_adapter::Load_fn load_fn,
10212 Parallel_reader_adapter::End_fn end_fn) {
10213 auto adapter = static_cast<Parallel_reader_adapter *>(scan_ctx);
10214
10215 auto err = adapter->run(thread_ctxs, init_fn, load_fn, end_fn);
10216
10217 return (convert_error_code_to_mysql(err, 0, ha_thd()));
10218 }
10219
10220 void ha_innopart::parallel_scan_end(void *parallel_scan_ctx) {
10221 auto adapter = static_cast<Parallel_reader_adapter *>(parallel_scan_ctx);
10222 ut::delete_(adapter);
10223 }
10224
10225 /** Check if InnoDB supports a particular alter table in-place.
10226 @param[in] altered_table TABLE object for new version of table.
10227 @param[in,out] ha_alter_info Structure describing changes to be done
10228 by ALTER TABLE and holding data used during in-place alter.
10229 @retval HA_ALTER_INPLACE_NOT_SUPPORTED Not supported
10230 @retval HA_ALTER_INPLACE_NO_LOCK Supported
10231 @retval HA_ALTER_INPLACE_SHARED_LOCK_AFTER_PREPARE Supported, but
10232 requires lock during main phase and exclusive lock during prepare
10233 phase.
10234 @retval HA_ALTER_INPLACE_NO_LOCK_AFTER_PREPARE Supported, prepare
10235 phase requires exclusive lock. */
10236 2922 enum_alter_inplace_result ha_innopart::check_if_supported_inplace_alter(
10237 TABLE *altered_table, Alter_inplace_info *ha_alter_info) {
10238
1/2
✓ Branch 0 taken 2922 times.
✗ Branch 1 not taken.
2922 DBUG_TRACE;
10239
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2922 times.
2922 assert(ha_alter_info->handler_ctx == nullptr);
10240
10241 /* Not supporting these for partitioned tables yet! */
10242
10243 /*
10244 FK not yet supported. SQL-layer blocks most of such changes.
10245 We resort to COPY algorithm for a few which are still allowed
10246 (e.g. REMOVE PARTITIONING and ADD FOREIGN KEY at the same time).
10247 */
10248
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2922 times.
2922 if (ha_alter_info->handler_flags & (Alter_inplace_info::ADD_FOREIGN_KEY |
10249 Alter_inplace_info::DROP_FOREIGN_KEY)) {
10250 ha_alter_info->unsupported_reason =
10251 innobase_get_err_msg(ER_FOREIGN_KEY_ON_PARTITIONED);
10252 return HA_ALTER_INPLACE_NOT_SUPPORTED;
10253 }
10254 /* FTS not yet supported either. */
10255
2/2
✓ Branch 0 taken 212 times.
✓ Branch 1 taken 2710 times.
2922 if ((ha_alter_info->handler_flags & Alter_inplace_info::ADD_INDEX)) {
10256
2/2
✓ Branch 0 taken 216 times.
✓ Branch 1 taken 212 times.
428 for (uint i = 0; i < ha_alter_info->index_add_count; i++) {
10257 216 const KEY *key =
10258 216 &ha_alter_info->key_info_buffer[ha_alter_info->index_add_buffer[i]];
10259
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 216 times.
216 if (key->flags & HA_FULLTEXT) {
10260 assert(!(key->flags & HA_KEYFLAG_MASK &
10261 ~(HA_FULLTEXT | HA_PACK_KEY | HA_GENERATED_KEY |
10262 HA_BINARY_PACK_KEY)));
10263 ha_alter_info->unsupported_reason =
10264 innobase_get_err_msg(ER_FULLTEXT_NOT_SUPPORTED_WITH_PARTITIONING);
10265 return HA_ALTER_INPLACE_NOT_SUPPORTED;
10266 }
10267 }
10268 }
10269 /* We cannot allow INPLACE to change order of KEY partitioning fields! */
10270 5844 if ((ha_alter_info->handler_flags &
10271
4/4
✓ Branch 0 taken 234 times.
✓ Branch 1 taken 2688 times.
✓ Branch 2 taken 7 times.
✓ Branch 3 taken 2915 times.
3156 Alter_inplace_info::ALTER_STORED_COLUMN_ORDER) &&
10272
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 227 times.
234 !m_part_info->same_key_column_order(
10273
1/2
✓ Branch 0 taken 234 times.
✗ Branch 1 not taken.
234 &ha_alter_info->alter_info->create_list)) {
10274 7 return HA_ALTER_INPLACE_NOT_SUPPORTED;
10275 }
10276
10277 /* Cannot allow INPLACE for drop and create PRIMARY KEY if partition is
10278 on Primary Key - PARTITION BY KEY() */
10279
2/2
✓ Branch 0 taken 183 times.
✓ Branch 1 taken 2732 times.
2915 if ((ha_alter_info->handler_flags & (Alter_inplace_info::ADD_PK_INDEX |
10280 Alter_inplace_info::DROP_PK_INDEX))) {
10281 /* Check partition by key(). */
10282 423 if ((m_part_info->part_type == partition_type::HASH) &&
10283
8/8
✓ Branch 0 taken 57 times.
✓ Branch 1 taken 126 times.
✓ Branch 2 taken 34 times.
✓ Branch 3 taken 23 times.
✓ Branch 4 taken 6 times.
✓ Branch 5 taken 28 times.
✓ Branch 6 taken 6 times.
✓ Branch 7 taken 177 times.
217 m_part_info->list_of_part_fields &&
10284 34 m_part_info->part_field_list.is_empty()) {
10285 6 return HA_ALTER_INPLACE_NOT_SUPPORTED;
10286 }
10287
10288 /* Check sub-partition by key(). */
10289 438 if ((m_part_info->subpart_type == partition_type::HASH) &&
10290
6/8
✓ Branch 0 taken 84 times.
✓ Branch 1 taken 93 times.
✓ Branch 2 taken 42 times.
✓ Branch 3 taken 42 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 42 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 177 times.
219 m_part_info->list_of_subpart_fields &&
10291 42 m_part_info->subpart_field_list.is_empty()) {
10292 return HA_ALTER_INPLACE_NOT_SUPPORTED;
10293 }
10294 }
10295
10296 /* Check for ALTER TABLE ... PARTITION, following operations can
10297 be done inplace */
10298
2/2
✓ Branch 0 taken 1027 times.
✓ Branch 1 taken 1882 times.
2909 if (alter_parts::apply_to(ha_alter_info)) {
10299 /* Two meanings here:
10300 1. ALTER TABLE .. PARTITION could not be combined with
10301 other ALTER TABLE operations;
10302 2. Only one operation of ALTER TABLE .. PARTITION can be
10303 done in single statement. Only exception is that
10304 'ALTER TABLE table REORGANIZE PARTITION' for HASH/KEY
10305 partitions. This will flag both COALESCE_PARTITION
10306 and ALTER_TABLE_REORG;
10307 The ALTER_ALL_PARTITION should be screened out, which could only
10308 be set along with the REBUILD PARTITION */
10309
2/6
✗ Branch 0 not taken.
✓ Branch 1 taken 1027 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 1027 times.
1027 ut_ad(is_single_bit(ha_alter_info->handler_flags &
10310 ~Alter_inplace_info::ALTER_ALL_PARTITION) ||
10311 ha_alter_info->handler_flags ==
10312 (Alter_inplace_info::COALESCE_PARTITION |
10313 Alter_inplace_info::ALTER_TABLE_REORG));
10314
4/6
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 1013 times.
✓ Branch 2 taken 14 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 1027 times.
1027 ut_ad(!(ha_alter_info->handler_flags &
10315 Alter_inplace_info::ALTER_ALL_PARTITION) ||
10316 (ha_alter_info->handler_flags &
10317 Alter_inplace_info::ALTER_REBUILD_PARTITION));
10318
10319
3/4
✓ Branch 0 taken 1027 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 668 times.
✓ Branch 3 taken 359 times.
1027 if (alter_parts::need_copy(ha_alter_info)) {
10320 668 return HA_ALTER_INPLACE_SHARED_LOCK_AFTER_PREPARE;
10321 } else {
10322 359 return HA_ALTER_INPLACE_NO_LOCK_AFTER_PREPARE;
10323 }
10324 }
10325
10326 3764 Instant_Type instant_type = innopart_support_instant(
10327
1/2
✓ Branch 0 taken 1882 times.
✗ Branch 1 not taken.
1882 ha_alter_info, m_tot_parts, m_part_share, this->table, altered_table);
10328 1882 ha_alter_info->handler_trivial_ctx =
10329 1882 instant_type_to_int(Instant_Type::INSTANT_IMPOSSIBLE);
10330
10331
3/4
✓ Branch 0 taken 1601 times.
✓ Branch 1 taken 277 times.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
1882 switch (instant_type) {
10332 1601 case Instant_Type::INSTANT_IMPOSSIBLE:
10333 1601 break;
10334 277 case Instant_Type::INSTANT_ADD_DROP_COLUMN:
10335
2/2
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 266 times.
277 if (ha_alter_info->alter_info->requested_algorithm ==
10336 Alter_info::ALTER_TABLE_ALGORITHM_INPLACE) {
10337 11 break;
10338
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 266 times.
266 } else if (m_prebuilt->table->n_def == REC_MAX_N_FIELDS) {
10339 if (ha_alter_info->alter_info->requested_algorithm ==
10340 Alter_info::ALTER_TABLE_ALGORITHM_INSTANT) {
10341 my_error(ER_TOO_MANY_FIELDS, MYF(0), m_prebuilt->table->name.m_name);
10342 return HA_ALTER_ERROR;
10343 }
10344 /* INSTANT can't be done any more. Fall back to INPLACE. */
10345 break;
10346
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 266 times.
266 } else if (m_prebuilt->table->current_row_version == MAX_ROW_VERSION) {
10347 if (ha_alter_info->alter_info->requested_algorithm ==
10348 Alter_info::ALTER_TABLE_ALGORITHM_INSTANT) {
10349 my_error(ER_INNODB_MAX_ROW_VERSION, MYF(0),
10350 m_prebuilt->table->name.m_name);
10351 return HA_ALTER_ERROR;
10352 }
10353 /* INSTANT can't be done any more. Fall back to INPLACE. */
10354 break;
10355
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 266 times.
266 } else if (!Instant_ddl_impl<dd::Table>::is_instant_add_possible(
10356
1/2
✓ Branch 0 taken 266 times.
✗ Branch 1 not taken.
266 ha_alter_info, table, altered_table, m_prebuilt->table)) {
10357 if (ha_alter_info->alter_info->requested_algorithm ==
10358 Alter_info::ALTER_TABLE_ALGORITHM_INSTANT) {
10359 /* Try to find if after adding columns, any possible row stays
10360 within permissible limit. If it doesn't, return error. */
10361 my_error(ER_INNODB_INSTANT_ADD_NOT_SUPPORTED_MAX_SIZE, MYF(0));
10362 return HA_ALTER_ERROR;
10363 }
10364
10365 /* INSTANT can't be done. Fall back to INPLACE. */
10366 break;
10367
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 266 times.
266 } else if (ha_alter_info->error_if_not_empty) {
10368 /* In this case, it can't be instant because the table
10369 may not be empty. Have to fall back to INPLACE */
10370 break;
10371 }
10372 [[fallthrough]];
10373 case Instant_Type::INSTANT_NO_CHANGE:
10374 case Instant_Type::INSTANT_VIRTUAL_ONLY:
10375 case Instant_Type::INSTANT_COLUMN_RENAME:
10376
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 269 times.
270 if (altered_table->s->fields > REC_MAX_N_USER_FIELDS) {
10377 /* Deny the inplace ALTER TABLE. MySQL will try to
10378 re-create the table and ha_innobase::create() will
10379 return an error too. This is how we effectively
10380 deny adding too many columns to a table. */
10381 1 ha_alter_info->unsupported_reason =
10382
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 innobase_get_err_msg(ER_TOO_MANY_FIELDS);
10383 1 return HA_ALTER_INPLACE_NOT_SUPPORTED;
10384 }
10385
10386 269 ha_alter_info->handler_trivial_ctx = instant_type_to_int(instant_type);
10387 269 return HA_ALTER_INPLACE_INSTANT;
10388 }
10389
10390 /* Check for PK and UNIQUE should already be done when creating the
10391 new table metadata.
10392 (fix_partition_info/check_primary_key+check_unique_key) */
10393
10394
1/2
✓ Branch 0 taken 1612 times.
✗ Branch 1 not taken.
1612 set_partition(0);
10395
1/2
✓ Branch 0 taken 1612 times.
✗ Branch 1 not taken.
1612 return ha_innobase::check_if_supported_inplace_alter(altered_table,
10396 1612 ha_alter_info);
10397 2922 }
10398
10399 /** Prepare in-place ALTER for table.
10400 Allows InnoDB to update internal structures with concurrent
10401 writes blocked (provided that check_if_supported_inplace_alter()
10402 did not return HA_ALTER_INPLACE_NO_LOCK).
10403 This will be invoked before inplace_alter_table().
10404 @param[in] altered_table TABLE object for new version of table.
10405 @param[in,out] ha_alter_info Structure describing changes to be done
10406 by ALTER TABLE and holding data used during in-place alter.
10407 @param[in] old_table_def dd::Table object describing old
10408 version of the table.
10409 @param[in,out] new_table_def dd::Table object for the new version
10410 of the table. Can be adjusted by this call. Changes to the table
10411 definition will be persisted in the data-dictionary at statement
10412 commit time.
10413 @retval true Failure.
10414 @retval false Success. */
10415 2566 bool ha_innopart::prepare_inplace_alter_table(TABLE *altered_table,
10416 Alter_inplace_info *ha_alter_info,
10417 const dd::Table *old_table_def,
10418 dd::Table *new_table_def) {
10419
1/2
✓ Branch 0 taken 2566 times.
✗ Branch 1 not taken.
2566 DBUG_TRACE;
10420
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2566 times.
2566 assert(ha_alter_info->handler_ctx == nullptr);
10421
10422
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2564 times.
2566 if (tablespace_is_shared_space(ha_alter_info->create_info)) {
10423
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 my_printf_error(ER_ILLEGAL_HA_CREATE_OPTION, PARTITION_IN_SHARED_TABLESPACE,
10424 MYF(0));
10425 2 return true;
10426 }
10427
10428 /* The row format in new table may differ from the old one,
10429 which is set by server earlier. So keep them the same */
10430
2/4
✓ Branch 0 taken 2564 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2564 times.
✗ Branch 3 not taken.
2564 new_table_def->set_row_format(old_table_def->row_format());
10431
10432
2/2
✓ Branch 0 taken 471 times.
✓ Branch 1 taken 2093 times.
2564 if (altered_table->found_next_number_field != nullptr) {
10433
2/4
✓ Branch 0 taken 471 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 471 times.
✗ Branch 3 not taken.
471 dd_copy_autoinc(old_table_def->se_private_data(),
10434
1/2
✓ Branch 0 taken 471 times.
✗ Branch 1 not taken.
471 new_table_def->se_private_data());
10435 }
10436
10437
2/2
✓ Branch 0 taken 1000 times.
✓ Branch 1 taken 1564 times.
2564 if (alter_parts::apply_to(ha_alter_info)) {
10438
1/2
✓ Branch 0 taken 925 times.
✗ Branch 1 not taken.
1000 return prepare_inplace_alter_partition(altered_table, ha_alter_info,
10439 925 old_table_def, new_table_def);
10440 }
10441
10442 ha_innopart_inplace_ctx *ctx_parts;
10443
1/2
✓ Branch 0 taken 1564 times.
✗ Branch 1 not taken.
1564 THD *thd = ha_thd();
10444 1564 bool res = true;
10445
10446 /* Clean up all ins/upd nodes. */
10447
1/2
✓ Branch 0 taken 1564 times.
✗ Branch 1 not taken.
1564 clear_ins_upd_nodes();
10448 /*
10449 This object will be freed by server, so always use 'new'
10450 and there is no need to free on failure */
10451
1/2
✓ Branch 0 taken 1564 times.
✗ Branch 1 not taken.
1564 ctx_parts = new (thd->mem_root) ha_innopart_inplace_ctx(m_tot_parts);
10452
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1564 times.
1564 if (ctx_parts == nullptr) {
10453 return HA_ALTER_ERROR;
10454 }
10455
10456
1/2
✓ Branch 0 taken 1564 times.
✗ Branch 1 not taken.
1564 ctx_parts->ctx_array = ut::new_arr_withkey<inplace_alter_handler_ctx *>(
10457 1564 UT_NEW_THIS_FILE_PSI_KEY, ut::Count{m_tot_parts + 1});
10458
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1564 times.
1564 if (ctx_parts->ctx_array == nullptr) {
10459 return HA_ALTER_ERROR;
10460 }
10461
10462 1564 memset(ctx_parts->ctx_array, 0,
10463 1564 sizeof(inplace_alter_handler_ctx *) * (m_tot_parts + 1));
10464
10465
1/2
✓ Branch 0 taken 1564 times.
✗ Branch 1 not taken.
1564 ctx_parts->m_old_info = ut::new_arr_withkey<alter_table_old_info_t>(
10466 1564 UT_NEW_THIS_FILE_PSI_KEY, ut::Count{m_tot_parts});
10467
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1564 times.
1564 if (ctx_parts->m_old_info == nullptr) {
10468 return HA_ALTER_ERROR;
10469 }
10470
10471
1/2
✓ Branch 0 taken 1564 times.
✗ Branch 1 not taken.
1564 ctx_parts->prebuilt_array = ut::new_arr_withkey<row_prebuilt_t *>(
10472 1564 UT_NEW_THIS_FILE_PSI_KEY, ut::Count{m_tot_parts});
10473
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1564 times.
1564 if (ctx_parts->prebuilt_array == nullptr) {
10474 return HA_ALTER_ERROR;
10475 }
10476 /* For the first partition use the current prebuilt. */
10477 1564 ctx_parts->prebuilt_array[0] = m_prebuilt;
10478 /* Create new prebuilt for the rest of the partitions.
10479 It is needed for the current implementation of
10480 ha_innobase::commit_inplace_alter_table(). */
10481
2/2
✓ Branch 0 taken 6742 times.
✓ Branch 1 taken 1564 times.
8306 for (uint i = 1; i < m_tot_parts; i++) {
10482 row_prebuilt_t *tmp_prebuilt;
10483
1/2
✓ Branch 0 taken 6742 times.
✗ Branch 1 not taken.
6742 tmp_prebuilt = row_create_prebuilt(m_part_share->get_table_part(i),
10484
1/2
✓ Branch 0 taken 6742 times.
✗ Branch 1 not taken.
6742 table_share->reclength);
10485 /* Use same trx as original prebuilt. */
10486 6742 tmp_prebuilt->trx = m_prebuilt->trx;
10487 6742 ctx_parts->prebuilt_array[i] = tmp_prebuilt;
10488 }
10489
10490
2/2
✓ Branch 0 taken 225 times.
✓ Branch 1 taken 1339 times.
1564 if (altered_table->found_next_number_field != nullptr) {
10491
1/2
✓ Branch 0 taken 225 times.
✗ Branch 1 not taken.
225 dd_set_autoinc(new_table_def->se_private_data(),
10492
1/2
✓ Branch 0 taken 225 times.
✗ Branch 1 not taken.
225 ha_alter_info->create_info->auto_increment_value);
10493 }
10494
10495 1564 const char *save_tablespace = ha_alter_info->create_info->tablespace;
10496
10497 1564 const char *save_data_file_name = ha_alter_info->create_info->data_file_name;
10498
10499
1/2
✓ Branch 0 taken 1564 times.
✗ Branch 1 not taken.
1564 auto oldp = old_table_def->leaf_partitions().begin();
10500
1/2
✓ Branch 0 taken 1564 times.
✗ Branch 1 not taken.
1564 auto newp = new_table_def->leaf_partitions()->begin();
10501
10502
2/2
✓ Branch 0 taken 8257 times.
✓ Branch 1 taken 1515 times.
9772 for (uint i = 0; i < m_tot_parts; ++oldp, ++newp) {
10503 8257 m_prebuilt = ctx_parts->prebuilt_array[i];
10504
1/2
✓ Branch 0 taken 8257 times.
✗ Branch 1 not taken.
8257 set_partition(i);
10505
10506 8257 const dd::Partition *old_part = *oldp;
10507 8257 dd::Partition *new_part = *newp;
10508
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8257 times.
8257 ut_ad(old_part != nullptr);
10509
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8257 times.
8257 ut_ad(new_part != nullptr);
10510
10511 /* We exempt this asserion when we do inplace during copy algorithm (ie.
10512 during expanded fast index creation). This is OK because we are using an
10513 intermediate table created during ALTER COPY algorithm. Hence
10514 m_prebuilt->table->id is newer than the original id stored in DD */
10515
5/8
✓ Branch 0 taken 8257 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 12 times.
✓ Branch 3 taken 8245 times.
✓ Branch 4 taken 12 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 8257 times.
8257 ut_ad(m_prebuilt->table->id == old_part->se_private_id() ||
10516 m_prebuilt->table->skip_alter_undo);
10517
10518 8257 ha_alter_info->handler_ctx = nullptr;
10519
10520 /* Set the tablespace and data_file_name value of the
10521 alter_info to the tablespace and data_file_name value
10522 that was existing for the partition originally, so that
10523 for ALTER TABLE the tablespace clause in create option
10524 is ignored for existing partitions, and later set it
10525 back to its old value */
10526
10527 8257 ha_alter_info->create_info->tablespace = m_prebuilt->table->tablespace;
10528 8257 ha_alter_info->create_info->data_file_name =
10529 8257 m_prebuilt->table->data_dir_path;
10530
10531
1/2
✓ Branch 0 taken 8212 times.
✗ Branch 1 not taken.
8257 res = prepare_inplace_alter_table_impl<dd::Partition>(
10532 altered_table, ha_alter_info, old_part, new_part);
10533
10534
1/2
✓ Branch 0 taken 8212 times.
✗ Branch 1 not taken.
8212 update_partition(i);
10535 8212 ctx_parts->ctx_array[i] = ha_alter_info->handler_ctx;
10536
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 8208 times.
8212 if (res) {
10537 4 break;
10538 }
10539
10540 8208 ha_innobase_inplace_ctx *ctx =
10541 8208 static_cast<ha_innobase_inplace_ctx *>(ctx_parts->ctx_array[i]);
10542
2/2
✓ Branch 0 taken 7053 times.
✓ Branch 1 taken 1155 times.
8208 if (ctx != nullptr) {
10543 7053 ctx_parts->m_old_info[i].update(ctx->old_table, ctx->need_rebuild());
10544 }
10545
10546 8208 ++i;
10547 }
10548
10549 1519 m_prebuilt = ctx_parts->prebuilt_array[0];
10550 1519 ha_alter_info->handler_ctx = ctx_parts;
10551 1519 ha_alter_info->group_commit_ctx = ctx_parts->ctx_array;
10552 1519 ha_alter_info->create_info->tablespace = save_tablespace;
10553 1519 ha_alter_info->create_info->data_file_name = save_data_file_name;
10554
10555 1519 return res;
10556 2446 }
10557
10558 2427 bool ha_innopart::inplace_alter_table(TABLE *altered_table,
10559 Alter_inplace_info *ha_alter_info,
10560 const dd::Table *old_table_def,
10561 dd::Table *new_table_def) {
10562
2/2
✓ Branch 0 taken 912 times.
✓ Branch 1 taken 1515 times.
2427 if (alter_parts::apply_to(ha_alter_info)) {
10563
1/2
✓ Branch 0 taken 912 times.
✗ Branch 1 not taken.
912 return (inplace_alter_partition(ha_alter_info));
10564 }
10565
10566 1515 bool res = true;
10567 ha_innopart_inplace_ctx *ctx_parts;
10568
10569 1515 ctx_parts =
10570 static_cast<ha_innopart_inplace_ctx *>(ha_alter_info->handler_ctx);
10571
10572 /* It could be not allocated at all */
10573
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1515 times.
1515 if (ctx_parts == nullptr) {
10574 return (false);
10575 }
10576
10577
1/2
✓ Branch 0 taken 1515 times.
✗ Branch 1 not taken.
1515 auto oldp = old_table_def->leaf_partitions().begin();
10578
1/2
✓ Branch 0 taken 1515 times.
✗ Branch 1 not taken.
1515 auto newp = new_table_def->leaf_partitions()->begin();
10579
10580
2/2
✓ Branch 0 taken 8163 times.
✓ Branch 1 taken 1513 times.
9676 for (uint i = 0; i < m_tot_parts; ++oldp, ++newp) {
10581 8163 m_prebuilt = ctx_parts->prebuilt_array[i];
10582 8163 ha_alter_info->handler_ctx = ctx_parts->ctx_array[i];
10583
1/2
✓ Branch 0 taken 8163 times.
✗ Branch 1 not taken.
8163 set_partition(i);
10584
4/4
✓ Branch 0 taken 6648 times.
✓ Branch 1 taken 1515 times.
✓ Branch 2 taken 5762 times.
✓ Branch 3 taken 886 times.
8163 if (i != 0 && ha_alter_info->handler_ctx != nullptr) {
10585
1/2
✓ Branch 0 taken 5762 times.
✗ Branch 1 not taken.
5762 ha_alter_info->handler_ctx->set_shared_data(ctx_parts->ctx_array[i - 1]);
10586 }
10587
10588
1/2
✓ Branch 0 taken 8163 times.
✗ Branch 1 not taken.
8163 res = inplace_alter_table_impl<dd::Partition>(altered_table, ha_alter_info);
10589
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8163 times.
8163 ut_ad(ctx_parts->ctx_array[i] == ha_alter_info->handler_ctx);
10590 8163 ctx_parts->ctx_array[i] = ha_alter_info->handler_ctx;
10591
10592
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 8161 times.
8163 if (res) {
10593 2 break;
10594 }
10595
10596 8161 ++i;
10597 }
10598 1515 m_prebuilt = ctx_parts->prebuilt_array[0];
10599 1515 ha_alter_info->handler_ctx = ctx_parts;
10600 1515 return (res);
10601 }
10602
10603 /** Commit or rollback.
10604 Commit or rollback the changes made during
10605 prepare_inplace_alter_table() and inplace_alter_table() inside
10606 the storage engine. Note that the allowed level of concurrency
10607 during this operation will be the same as for
10608 inplace_alter_table() and thus might be higher than during
10609 prepare_inplace_alter_table(). (E.g concurrent writes were
10610 blocked during prepare, but might not be during commit).
10611 @param[in] altered_table TABLE object for new version of table.
10612 @param[in,out] ha_alter_info Structure describing changes to be done
10613 by ALTER TABLE and holding data used during
10614 in-place alter.
10615 @param[in] commit true => Commit, false => Rollback.
10616 @param[in] old_table_def dd::Table object describing old
10617 version of the table.
10618 @param[in,out] new_table_def dd::Table object for the new version
10619 of the table. Can be adjusted by this call. Changes to the table
10620 definition will be persisted in the data-dictionary at statement
10621 commit time.
10622 @retval true Failure.
10623 @retval false Success. */
10624 2446 bool ha_innopart::commit_inplace_alter_table(TABLE *altered_table,
10625 Alter_inplace_info *ha_alter_info,
10626 bool commit,
10627 const dd::Table *old_table_def,
10628 dd::Table *new_table_def) {
10629
2/2
✓ Branch 0 taken 925 times.
✓ Branch 1 taken 1521 times.
2446 if (alter_parts::apply_to(ha_alter_info)) {
10630 925 return (commit_inplace_alter_partition(altered_table, ha_alter_info, commit,
10631 875 old_table_def, new_table_def));
10632 }
10633
10634 1521 ha_innopart_inplace_ctx *ctx_parts =
10635 static_cast<ha_innopart_inplace_ctx *>(ha_alter_info->handler_ctx);
10636
10637 /* It could be not allocated at all */
10638
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1519 times.
1521 if (ctx_parts == nullptr) {
10639 2 return (false);
10640 }
10641
10642 1519 bool res = false;
10643
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1519 times.
1519 ut_ad(ctx_parts->ctx_array != nullptr);
10644
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1519 times.
1519 ut_ad(ctx_parts->prebuilt_array != nullptr);
10645
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1519 times.
1519 ut_ad(ctx_parts->prebuilt_array[0] == m_prebuilt);
10646
10647
2/2
✓ Branch 0 taken 1513 times.
✓ Branch 1 taken 6 times.
1519 if (commit) {
10648 /* Commit is done through first partition (group commit). */
10649
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1513 times.
1513 ut_ad(ha_alter_info->group_commit_ctx == ctx_parts->ctx_array);
10650 1513 ha_alter_info->handler_ctx = ctx_parts->ctx_array[0];
10651 1513 set_partition(0);
10652
10653 1513 res = ha_innobase::commit_inplace_alter_table_impl<dd::Table>(
10654 altered_table, ha_alter_info, commit, new_table_def);
10655
3/6
✓ Branch 0 taken 1482 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1482 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 1482 times.
1482 ut_ad(res || !ha_alter_info->group_commit_ctx);
10656
10657 1482 goto end;
10658 }
10659
10660 /* Rollback is done for each partition. */
10661
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 6 times.
18 for (uint i = 0; i < m_tot_parts; i++) {
10662 12 m_prebuilt = ctx_parts->prebuilt_array[i];
10663 12 ha_alter_info->handler_ctx = ctx_parts->ctx_array[i];
10664 12 set_partition(i);
10665
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
12 if (ha_innobase::commit_inplace_alter_table_impl<dd::Table>(
10666 altered_table, ha_alter_info, commit, new_table_def)) {
10667 res = true;
10668 }
10669
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
12 ut_ad(ctx_parts->ctx_array[i] == ha_alter_info->handler_ctx);
10670 12 ctx_parts->ctx_array[i] = ha_alter_info->handler_ctx;
10671 }
10672 6 end:
10673 /* All are done successfully, now write back metadata to DD */
10674
3/4
✓ Branch 0 taken 1482 times.
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 1482 times.
✗ Branch 3 not taken.
1488 if (commit && !res) {
10675
4/6
✓ Branch 0 taken 269 times.
✓ Branch 1 taken 1213 times.
✓ Branch 2 taken 269 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 1482 times.
1482 ut_ad(!(is_instant(ha_alter_info) && ctx_parts->m_old_info[0].m_rebuild));
10676
10677
1/2
✓ Branch 0 taken 1482 times.
✗ Branch 1 not taken.
1482 auto oldp = old_table_def->leaf_partitions().begin();
10678
1/2
✓ Branch 0 taken 1482 times.
✗ Branch 1 not taken.
1482 auto newp = new_table_def->leaf_partitions()->begin();
10679 1482 bool inplace_instant = false;
10680
10681
2/2
✓ Branch 0 taken 8066 times.
✓ Branch 1 taken 1482 times.
9548 for (uint i = 0; i < m_tot_parts; ++oldp, ++newp) {
10682 8066 const dd::Partition *old_part = *oldp;
10683 8066 dd::Partition *new_part = *newp;
10684
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8066 times.
8066 ut_ad(old_part != nullptr);
10685
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8066 times.
8066 ut_ad(new_part != nullptr);
10686
10687 8066 ha_innobase_inplace_ctx *ctx =
10688 8066 static_cast<ha_innobase_inplace_ctx *>(ctx_parts->ctx_array[i]);
10689
10690
2/2
✓ Branch 0 taken 1155 times.
✓ Branch 1 taken 6911 times.
8066 if (is_instant(ha_alter_info)) {
10691 Instant_ddl_impl<dd::Partition> executor(
10692 1155 ha_alter_info, m_user_thd, m_prebuilt->trx,
10693 1155 m_part_share->get_table_part(i), table, altered_table, old_part,
10694 new_part,
10695 1155 altered_table->found_next_number_field != nullptr
10696 115 ? reinterpret_cast<uint64_t *>(&m_part_share->next_auto_inc_val)
10697
3/4
✓ Branch 0 taken 115 times.
✓ Branch 1 taken 1040 times.
✓ Branch 2 taken 1155 times.
✗ Branch 3 not taken.
1155 : nullptr);
10698
10699 /* Execute Instant DDL */
10700
1/2
✓ Branch 0 taken 1155 times.
✗ Branch 1 not taken.
1155 executor.commit_instant_ddl();
10701
2/4
✓ Branch 0 taken 6911 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 6911 times.
8066 } else if (!(ha_alter_info->handler_flags & ~INNOBASE_INPLACE_IGNORE) ||
10702 ctx == nullptr) {
10703 dd_commit_inplace_no_change(ha_alter_info, old_part, new_part, true);
10704 } else {
10705 6911 inplace_instant = !ctx_parts->m_old_info[0].m_rebuild;
10706
10707 /* Table is not rebuilt so copy instant metadata.
10708 NOTE : To be done only for first partition */
10709
4/4
✓ Branch 0 taken 1213 times.
✓ Branch 1 taken 5698 times.
✓ Branch 2 taken 193 times.
✓ Branch 3 taken 1020 times.
6911 if (i == 0 && inplace_instant) {
10710 193 dd_inplace_alter_copy_instant_metadata(
10711
1/2
✓ Branch 0 taken 193 times.
✗ Branch 1 not taken.
193 ha_alter_info, &old_part->table(),
10712
2/4
✓ Branch 0 taken 193 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 193 times.
✗ Branch 3 not taken.
193 const_cast<dd::Table *>(&new_part->table()));
10713 }
10714
10715
1/2
✓ Branch 0 taken 6911 times.
✗ Branch 1 not taken.
6911 dd_commit_inplace_alter_table(ctx_parts->m_old_info[i], ctx->new_table,
10716 old_part, new_part);
10717 }
10718
10719 8066 ++i;
10720 }
10721
10722 #ifdef UNIV_DEBUG
10723
1/2
✓ Branch 0 taken 1482 times.
✗ Branch 1 not taken.
1482 if (!res) {
10724
5/6
✓ Branch 0 taken 1482 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 208 times.
✓ Branch 3 taken 1274 times.
✓ Branch 4 taken 202 times.
✓ Branch 5 taken 1280 times.
1690 if (dd_table_has_instant_cols(*old_table_def) &&
10725
2/2
✓ Branch 0 taken 202 times.
✓ Branch 1 taken 6 times.
208 !ctx_parts->m_old_info[0].m_rebuild) {
10726
2/4
✓ Branch 0 taken 202 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 202 times.
202 ut_ad(dd_table_has_instant_cols(*new_table_def));
10727 }
10728
10729 1482 uint i = 0;
10730
3/4
✓ Branch 0 taken 1482 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 8066 times.
✓ Branch 3 taken 1482 times.
9548 for (auto part : *new_table_def->leaf_partitions()) {
10731 ha_innobase_inplace_ctx *ctx =
10732 8066 static_cast<ha_innobase_inplace_ctx *>(ctx_parts->ctx_array[i++]);
10733
2/2
✓ Branch 0 taken 6911 times.
✓ Branch 1 taken 1155 times.
8066 if (ctx != nullptr) {
10734
2/4
✓ Branch 0 taken 6911 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 6911 times.
6911 ut_ad(dd_table_match(ctx->new_table, part));
10735 }
10736 }
10737 }
10738 #endif /* univ_debug */
10739 }
10740
10741 /* Move the ownership of the new tables back to the m_part_share. */
10742 ha_innobase_inplace_ctx *ctx;
10743
2/2
✓ Branch 0 taken 7188 times.
✓ Branch 1 taken 1215 times.
8403 for (uint i = 0; i < m_tot_parts; i++) {
10744 /* TODO: Fix to only use one prebuilt (i.e. make inplace
10745 alter partition aware instead of using multiple prebuilt
10746 copies... */
10747 7188 ctx = static_cast<ha_innobase_inplace_ctx *>(ctx_parts->ctx_array[i]);
10748
2/2
✓ Branch 0 taken 6915 times.
✓ Branch 1 taken 273 times.
7188 if (ctx != nullptr) {
10749 6915 m_part_share->set_table_part(i, ctx->prebuilt->table);
10750 6915 ctx->prebuilt->table = nullptr;
10751 6915 ctx_parts->prebuilt_array[i] = ctx->prebuilt;
10752 } else {
10753 273 break;
10754 }
10755 }
10756 /* The above juggling of prebuilt must be reset here. */
10757 1488 m_prebuilt = ctx_parts->prebuilt_array[0];
10758 1488 m_prebuilt->table = m_part_share->get_table_part(0);
10759 1488 ha_alter_info->handler_ctx = ctx_parts;
10760 1488 return (res);
10761 }
10762
10763 /** Create the Altered_partitoins object
10764 @param[in] ha_alter_info thd DDL operation
10765 @retval true On failure
10766 @retval false On success */
10767 648 bool ha_innopart::prepare_for_copy_partitions(
10768 Alter_inplace_info *ha_alter_info) {
10769
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 648 times.
648 ut_ad(m_new_partitions == nullptr);
10770
2/4
✓ Branch 0 taken 648 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 648 times.
648 ut_ad(alter_parts::need_copy(ha_alter_info));
10771
10772 648 uint num_parts = ha_alter_info->modified_part_info->num_parts;
10773 648 uint total_parts = num_parts;
10774
10775
2/2
✓ Branch 0 taken 78 times.
✓ Branch 1 taken 570 times.
648 if (ha_alter_info->modified_part_info->is_sub_partitioned()) {
10776 78 total_parts *= ha_alter_info->modified_part_info->num_subparts;
10777 }
10778
10779
1/2
✓ Branch 0 taken 648 times.
✗ Branch 1 not taken.
648 m_new_partitions = ut::new_withkey<Altered_partitions>(
10780 ut::make_psi_memory_key(mem_key_partitioning), total_parts);
10781
10782
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 648 times.
648 if (m_new_partitions == nullptr) {
10783 return (true);
10784
2/4
✓ Branch 0 taken 648 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 648 times.
648 } else if (m_new_partitions->initialize()) {
10785 ut::delete_(m_new_partitions);
10786 m_new_partitions = nullptr;
10787 return (true);
10788 }
10789
10790 648 return (false);
10791 }
10792
10793 /** write row to new partition.
10794 @param[in] new_part New partition to write to.
10795 @return 0 for success else error code. */
10796 3255 int ha_innopart::write_row_in_new_part(uint new_part) {
10797 int result;
10798
1/2
✓ Branch 0 taken 3255 times.
✗ Branch 1 not taken.
3255 DBUG_TRACE;
10799
10800 3255 m_last_part = new_part;
10801
3/4
✓ Branch 0 taken 3255 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
✓ Branch 3 taken 3250 times.
3255 if (m_new_partitions->part(new_part) == nullptr) {
10802 /* Altered partition contains misplaced row. */
10803 5 m_err_rec = table->record[0];
10804 5 return HA_ERR_ROW_IN_WRONG_PARTITION;
10805 }
10806
10807
1/2
✓ Branch 0 taken 3250 times.
✗ Branch 1 not taken.
3250 m_new_partitions->prepare_write(m_prebuilt, new_part);
10808
1/2
✓ Branch 0 taken 3250 times.
✗ Branch 1 not taken.
3250 result = ha_innobase::write_row(table->record[0]);
10809
1/2
✓ Branch 0 taken 3250 times.
✗ Branch 1 not taken.
3250 m_new_partitions->finish_write(m_prebuilt, new_part);
10810 3250 return result;
10811 3255 }
10812
10813 /** Allows InnoDB to update internal structures with concurrent
10814 writes blocked (given that check_if_supported_inplace_alter()
10815 did not return HA_ALTER_INPLACE_NO_LOCK).
10816 This is for 'ALTER TABLE ... PARTITION' and a corresponding function
10817 to inplace_alter_table().
10818 This will be invoked before inplace_alter_partition().
10819
10820 @param[in,out] altered_table TABLE object for new version of table
10821 @param[in,out] ha_alter_info Structure describing changes to be done
10822 by ALTER TABLE and holding data used during
10823 in-place alter.
10824 @param[in] old_dd_tab Table definition before the ALTER
10825 @param[in,out] new_dd_tab Table definition after the ALTER
10826 @retval true Failure
10827 @retval false Success */
10828 1000 bool ha_innopart::prepare_inplace_alter_partition(
10829 TABLE *altered_table, Alter_inplace_info *ha_alter_info,
10830 const dd::Table *old_dd_tab, dd::Table *new_dd_tab) {
10831 1000 clear_ins_upd_nodes();
10832
10833 1000 trx_start_if_not_started_xa(m_prebuilt->trx, true, UT_LOCATION_HERE);
10834
10835
3/4
✓ Branch 0 taken 648 times.
✓ Branch 1 taken 352 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1000 times.
1648 if (alter_parts::need_copy(ha_alter_info) &&
10836
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 648 times.
648 prepare_for_copy_partitions(ha_alter_info)) {
10837 my_error(ER_OUT_OF_RESOURCES, MYF(0));
10838 return (true);
10839 }
10840
10841 1000 alter_parts *ctx = ut::new_withkey<alter_parts>(
10842 1000 UT_NEW_THIS_FILE_PSI_KEY, m_prebuilt->trx, m_part_share, ha_alter_info,
10843 1000 m_part_info, m_new_partitions);
10844
10845
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1000 times.
1000 if (ctx == nullptr) {
10846 my_error(ER_OUT_OF_RESOURCES, MYF(0));
10847 return (true);
10848 }
10849
10850 1000 ha_alter_info->handler_ctx = ctx;
10851
10852 1000 int error = ctx->prepare(*old_dd_tab, *new_dd_tab, altered_table);
10853
2/2
✓ Branch 0 taken 13 times.
✓ Branch 1 taken 912 times.
925 if (error != 0) {
10854
1/2
✓ Branch 0 taken 13 times.
✗ Branch 1 not taken.
13 print_error(error, MYF(error != ER_OUTOFMEMORY ? 0 : ME_FATALERROR));
10855 }
10856 925 return (error);
10857 }
10858
10859 912 bool ha_innopart::inplace_alter_partition(Alter_inplace_info *ha_alter_info) {
10860
3/4
✓ Branch 0 taken 912 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 334 times.
✓ Branch 3 taken 578 times.
912 if (!alter_parts::need_copy(ha_alter_info)) {
10861 334 return (false);
10862 }
10863
10864 /* The lock type can be set as none, since in this step, the
10865 shared table lock is held, thus no other changes. This is to fix
10866 if the table was explicitly lock, then select_lock_type in the
10867 prebuilt here would not be LOCK_NONE, then row locks would be
10868 required; if we finally want to drop the original partitions,
10869 these row locks would lead to failure/crash. */
10870 578 ulint lock_type = m_prebuilt->select_lock_type;
10871 578 m_prebuilt->select_lock_type = LOCK_NONE;
10872
10873
1/2
✓ Branch 0 taken 578 times.
✗ Branch 1 not taken.
578 prepare_change_partitions();
10874
10875 578 partition_info *old_part_info = table->part_info;
10876
10877
1/2
✓ Branch 0 taken 578 times.
✗ Branch 1 not taken.
578 set_part_info(ha_alter_info->modified_part_info, true);
10878
10879
1/2
✓ Branch 0 taken 578 times.
✗ Branch 1 not taken.
578 prepare_change_partitions();
10880
10881 ulonglong deleted;
10882 int res;
10883
10884
1/2
✓ Branch 0 taken 578 times.
✗ Branch 1 not taken.
578 res = copy_partitions(&deleted);
10885
10886
1/2
✓ Branch 0 taken 578 times.
✗ Branch 1 not taken.
578 set_part_info(old_part_info, false);
10887
10888 578 m_prebuilt->select_lock_type = lock_type;
10889
10890
2/2
✓ Branch 0 taken 29 times.
✓ Branch 1 taken 549 times.
578 if (res > 0) {
10891
2/4
✓ Branch 0 taken 29 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 29 times.
✗ Branch 3 not taken.
29 print_error(res, MYF(res != ER_OUTOFMEMORY ? 0 : ME_FATALERROR));
10892 }
10893
10894 578 return (res);
10895 }
10896
10897 /** Prepare to commit or roll back ALTER TABLE...ALGORITHM=INPLACE.
10898 This is for 'ALTER TABLE ... PARTITION' and a corresponding function
10899 to commit_inplace_alter_table().
10900 @param[in,out] altered_table TABLE object for new version of table.
10901 @param[in,out] ha_alter_info ALGORITHM=INPLACE metadata
10902 @param[in] commit true=Commit, false=Rollback.
10903 @param[in] old_dd_tab old table
10904 @param[in,out] new_dd_tab new table
10905 @retval true on failure (my_error() will have been called)
10906 @retval false on success */
10907 925 bool ha_innopart::commit_inplace_alter_partition(
10908 TABLE *altered_table, Alter_inplace_info *ha_alter_info, bool commit,
10909 const dd::Table *old_dd_tab, dd::Table *new_dd_tab) {
10910 925 alter_parts *ctx = static_cast<alter_parts *>(ha_alter_info->handler_ctx);
10911 925 m_prebuilt->table = nullptr;
10912
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 925 times.
925 if (ctx == nullptr) {
10913 ut_ad(!commit);
10914 return (false);
10915 }
10916
10917
2/2
✓ Branch 0 taken 883 times.
✓ Branch 1 taken 42 times.
925 if (commit) {
10918 883 int error = ctx->try_commit(*old_dd_tab, *new_dd_tab, table, altered_table);
10919
1/2
✓ Branch 0 taken 833 times.
✗ Branch 1 not taken.
833 if (!error) {
10920 833 ut::delete_(ctx);
10921 833 ha_alter_info->handler_ctx = nullptr;
10922
10923 833 ut::delete_(m_new_partitions);
10924 833 m_new_partitions = nullptr;
10925
10926
2/2
✓ Branch 0 taken 121 times.
✓ Branch 1 taken 712 times.
833 if (altered_table->found_next_number_field) {
10927 121 dd_set_autoinc(new_dd_tab->se_private_data(),
10928 121 m_part_share->next_auto_inc_val);
10929 }
10930
10931 833 dd_copy_table(ha_alter_info, *new_dd_tab, *old_dd_tab);
10932 833 dd_part_adjust_table_id(new_dd_tab);
10933
10934
2/2
✓ Branch 0 taken 46 times.
✓ Branch 1 taken 787 times.
833 if (dd_table_has_instant_cols(*old_dd_tab)) {
10935 46 dd_inplace_alter_copy_instant_metadata(ha_alter_info, old_dd_tab,
10936 new_dd_tab);
10937 }
10938 }
10939
10940 833 return (error != 0);
10941 }
10942
10943 42 ctx->rollback();
10944 42 ut::delete_(ctx);
10945 42 ha_alter_info->handler_ctx = nullptr;
10946
10947 42 ut::delete_(m_new_partitions);
10948 42 m_new_partitions = nullptr;
10949
10950 42 return (false);
10951 }
10952
10953 /** Check if the DATA DIRECTORY is specified (implicitly or explicitly)
10954 @param[in] dd_part The dd::Partition to be checked
10955 @retval true the DATA DIRECTORY is specified (implicitly or explicitly)
10956 @retval false otherwise */
10957 208 static bool dd_part_has_datadir(const dd::Partition *dd_part) {
10958
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 208 times.
208 ut_ad(dd_part_is_stored(dd_part));
10959
10960
1/2
✓ Branch 0 taken 208 times.
✗ Branch 1 not taken.
208 return (dd_part->options().exists(data_file_name_key) ||
10961
3/4
✓ Branch 0 taken 180 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 66 times.
✓ Branch 3 taken 114 times.
180 (dd_part->parent() != nullptr &&
10962
7/12
✓ Branch 0 taken 208 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 180 times.
✓ Branch 3 taken 28 times.
✓ Branch 4 taken 66 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 66 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 66 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 66 times.
✗ Branch 11 not taken.
662 dd_part->parent()->options().exists(data_file_name_key)) ||
10963
4/10
✓ Branch 0 taken 180 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 180 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 180 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 180 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
568 dd_part->table().se_private_data().exists(
10964
3/4
✓ Branch 0 taken 180 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 180 times.
✓ Branch 3 taken 28 times.
596 dd_table_key_strings[DD_TABLE_DATA_DIRECTORY]));
10965 }
10966
10967 /** Adjust data directory for exchange partition. Special handling of
10968 dict_table_t::data_dir_path is necessary if DATA DIRECTORY is specified. For
10969 exaple if DATA DIRECTORY Is '/tmp', the data directory for nomral table is
10970 '/tmp/t1', while for partition is '/tmp'. So rename, the postfix table name 't1'
10971 should either be truncated or appended.
10972 @param[in] table_p partiton table
10973 @param[in] table_s swap table*/
10974 28 void exchange_partition_adjust_datadir(dict_table_t *table_p,
10975 dict_table_t *table_s) {
10976
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 28 times.
28 ut_ad(table_s->n_ref_count == 1);
10977
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 28 times.
28 ut_ad(table_p->n_ref_count == 1);
10978
1/2
✓ Branch 0 taken 28 times.
✗ Branch 1 not taken.
28 if (table_s->data_dir_path != nullptr) {
10979
1/2
✓ Branch 0 taken 28 times.
✗ Branch 1 not taken.
28 std::string str(table_s->data_dir_path);
10980 /* new_name contains database/name but we require name */
10981 28 const char *name = strchr(table_s->name.m_name, '/') + 1;
10982
1/2
✓ Branch 0 taken 28 times.
✗ Branch 1 not taken.
28 str.append(name);
10983
10984
1/2
✓ Branch 0 taken 28 times.
✗ Branch 1 not taken.
28 ulint old_size = mem_heap_get_size(table_s->heap);
10985
10986
1/2
✓ Branch 0 taken 28 times.
✗ Branch 1 not taken.
28 table_s->data_dir_path = mem_heap_strdup(table_s->heap, str.c_str());
10987
10988
1/2
✓ Branch 0 taken 28 times.
✗ Branch 1 not taken.
28 ulint new_size = mem_heap_get_size(table_s->heap);
10989
1/2
✓ Branch 0 taken 28 times.
✗ Branch 1 not taken.
28 dict_sys_mutex_enter();
10990 28 dict_sys->size += new_size - old_size;
10991
1/2
✓ Branch 0 taken 28 times.
✗ Branch 1 not taken.
28 dict_sys_mutex_exit();
10992 28 }
10993
10994
2/2
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 17 times.
28 if (table_p->data_dir_path != nullptr) {
10995
1/2
✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
11 std::string str(table_p->data_dir_path);
10996 11 size_t found = str.find_last_of("/\\");
10997
10998
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
11 ut_ad(found != std::string::npos);
10999 11 found++;
11000
11001 11 table_p->data_dir_path[found] = '\0';
11002 11 }
11003 28 }
11004
11005 /** Exchange partition.
11006 Low-level primitive which implementation is provided here.
11007 @param[in] part_id The id of the partition to be exchanged
11008 @param[in] part_table partitioned table to be exchanged
11009 @param[in] swap_table table to be exchanged
11010 @return error number
11011 @retval 0 on success */
11012 223 int ha_innopart::exchange_partition_low(uint part_id, dd::Table *part_table,
11013 dd::Table *swap_table) {
11014
1/2
✓ Branch 0 taken 223 times.
✗ Branch 1 not taken.
223 DBUG_TRACE;
11015
11016
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 223 times.
223 ut_ad(part_table != nullptr);
11017
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 223 times.
223 ut_ad(swap_table != nullptr);
11018
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 223 times.
223 ut_ad(m_part_share != nullptr);
11019
2/4
✓ Branch 0 taken 223 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 223 times.
223 ut_ad(dd_table_is_partitioned(*part_table));
11020
2/4
✓ Branch 0 taken 223 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 223 times.
223 ut_ad(!dd_table_is_partitioned(*swap_table));
11021
3/6
✓ Branch 0 taken 223 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 223 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 223 times.
223 ut_ad(innobase_strcasecmp(part_table->name().c_str(),
11022 table_share->table_name.str) == 0);
11023
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 223 times.
223 ut_ad(part_id < m_tot_parts);
11024 223 std::vector<dd::Partition_index *> part_indexes;
11025 223 std::vector<dd::Partition_index *>::iterator p_iter;
11026 223 std::vector<dd::Index *> swap_indexes;
11027 223 std::vector<dd::Index *>::iterator s_iter;
11028 #ifdef UNIV_DEBUG
11029 223 std::vector<dd::Index *> part_table_indexes;
11030 223 std::vector<dd::Index *>::iterator pt_iter;
11031 #endif
11032
11033
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 223 times.
223 if (high_level_read_only) {
11034 my_error(ER_READ_ONLY_MODE, MYF(0));
11035 return HA_ERR_TABLE_READONLY;
11036 }
11037
11038
5/6
✓ Branch 0 taken 223 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 220 times.
✓ Branch 3 taken 3 times.
✓ Branch 4 taken 6 times.
✓ Branch 5 taken 217 times.
443 if (dd_table_has_instant_cols(*part_table) ||
11039
3/4
✓ Branch 0 taken 220 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 217 times.
220 dd_table_has_instant_cols(*swap_table)) {
11040
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
6 my_error(ER_PARTITION_EXCHANGE_DIFFERENT_OPTION, MYF(0),
11041 "INSTANT COLUMN(s)");
11042 6 return true;
11043 }
11044
11045 /* Find the specified dd::Partition object */
11046 217 uint id = 0;
11047 217 dd::Partition *dd_part = nullptr;
11048
2/4
✓ Branch 0 taken 217 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 434 times.
✗ Branch 3 not taken.
434 for (auto part : *part_table->leaf_partitions()) {
11049
1/2
✓ Branch 0 taken 434 times.
✗ Branch 1 not taken.
434 ut_d(dict_table_t *table = m_part_share->get_table_part(id));
11050
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 434 times.
434 ut_ad(table->n_ref_count == 1);
11051
2/4
✓ Branch 0 taken 434 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 434 times.
434 ut_ad(!table->is_temporary());
11052
11053
2/2
✓ Branch 0 taken 217 times.
✓ Branch 1 taken 217 times.
434 if (++id > part_id) {
11054 217 dd_part = part;
11055 217 break;
11056 }
11057 }
11058
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 217 times.
217 ut_ad(dd_part != nullptr);
11059
11060
4/8
✓ Branch 0 taken 217 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 217 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 217 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 217 times.
434 if (dd_part->options().exists(index_file_name_key) ||
11061
3/6
✓ Branch 0 taken 217 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 217 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 217 times.
217 swap_table->options().exists(index_file_name_key)) {
11062 my_error(ER_PARTITION_EXCHANGE_DIFFERENT_OPTION, MYF(0), "INDEX DIRECTORY");
11063 ut_d(ut_error);
11064 ut_o(return true);
11065 }
11066
11067 /* Get the innodb table objects of part_table and swap_table */
11068
1/2
✓ Branch 0 taken 217 times.
✗ Branch 1 not taken.
217 const table_id_t table_id = swap_table->se_private_id();
11069
1/2
✓ Branch 0 taken 217 times.
✗ Branch 1 not taken.
217 dict_table_t *part = m_part_share->get_table_part(part_id);
11070 dict_table_t *swap;
11071 217 const auto hash_value = ut::hash_uint64(table_id);
11072
11073
1/2
✓ Branch 0 taken 217 times.
✗ Branch 1 not taken.
217 dict_sys_mutex_enter();
11074
7/12
✓ Branch 0 taken 217 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 217 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 217 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 222 times.
✓ Branch 8 taken 217 times.
✓ Branch 9 taken 5 times.
✓ Branch 10 taken 222 times.
✗ Branch 11 not taken.
222 HASH_SEARCH(id_hash, dict_sys->table_id_hash, hash_value, dict_table_t *,
11075 swap, ut_ad(swap->cached), swap->id == table_id);
11076
1/2
✓ Branch 0 taken 217 times.
✗ Branch 1 not taken.
217 dict_sys_mutex_exit();
11077
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 217 times.
217 ut_ad(swap != nullptr);
11078
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 217 times.
217 ut_ad(swap->n_ref_count == 1);
11079
11080 #ifdef UNIV_DEBUG
11081 /* Store and sort part_table indexes */
11082
6/12
✓ Branch 0 taken 217 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 217 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 217 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 217 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 217 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 217 times.
✗ Branch 11 not taken.
217 std::copy(part_table->indexes()->begin(), part_table->indexes()->end(),
11083 std::back_inserter(part_table_indexes));
11084
1/2
✓ Branch 0 taken 217 times.
✗ Branch 1 not taken.
217 std::sort(part_table_indexes.begin(), part_table_indexes.end(),
11085 99 [](dd::Index *a, dd::Index *b) { return (a->name() < b->name()); });
11086 #endif
11087
1/2
✓ Branch 0 taken 217 times.
✗ Branch 1 not taken.
217 dd::Object_id p_se_id = dd_part->se_private_id();
11088
11089 /* Try to rename files. Tablespace checking ensures that
11090 both partition and table are of implicit tablespace. The plan is:
11091 1. Rename the swap table to the intermediate file
11092 2. Rename the partition to the swap table file
11093 3. Rename the intermediate file of swap table to the partition file */
11094 217 THD *thd = m_prebuilt->trx->mysql_thd;
11095 217 char *swap_name = strdup(swap->name.m_name);
11096 217 char *part_name = strdup(part->name.m_name);
11097
11098 /* Define the temporary table name, by appending TMP_POSTFIX */
11099 char temp_name[FN_REFLEN];
11100 217 snprintf(temp_name, sizeof temp_name, "%s%s", swap_name,
11101 dict_name::TMP_POSTFIX);
11102
11103 217 int error = 0;
11104
1/2
✓ Branch 0 taken 214 times.
✗ Branch 1 not taken.
217 error = innobase_basic_ddl::rename_impl<dd::Table>(
11105 thd, swap_name, temp_name, swap_table, swap_table, nullptr);
11106
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 214 times.
214 if (error != 0) {
11107 goto func_exit;
11108 }
11109
1/2
✓ Branch 0 taken 211 times.
✗ Branch 1 not taken.
214 error = innobase_basic_ddl::rename_impl<dd::Partition>(
11110 thd, part_name, swap_name, dd_part, dd_part, nullptr);
11111
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 211 times.
211 if (error != 0) {
11112 goto func_exit;
11113 }
11114
1/2
✓ Branch 0 taken 208 times.
✗ Branch 1 not taken.
211 error = innobase_basic_ddl::rename_impl<dd::Table>(
11115 thd, temp_name, part_name, swap_table, swap_table, nullptr);
11116
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 208 times.
208 if (error != 0) {
11117 goto func_exit;
11118 }
11119
11120
5/6
✓ Branch 0 taken 208 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 180 times.
✓ Branch 3 taken 28 times.
✓ Branch 4 taken 28 times.
✓ Branch 5 taken 180 times.
388 if (dd_part_has_datadir(dd_part) ||
11121
3/6
✓ Branch 0 taken 180 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 180 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 180 times.
180 swap_table->options().exists(data_file_name_key)) {
11122 /* after above swaping swap is now partition table and part is now normal
11123 table */
11124
1/2
✓ Branch 0 taken 28 times.
✗ Branch 1 not taken.
28 exchange_partition_adjust_datadir(swap, part);
11125 }
11126
11127
6/12
✓ Branch 0 taken 208 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 208 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 208 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 208 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 208 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 208 times.
✗ Branch 11 not taken.
208 std::copy(dd_part->indexes()->begin(), dd_part->indexes()->end(),
11128 std::back_inserter(part_indexes));
11129
6/12
✓ Branch 0 taken 208 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 208 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 208 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 208 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 208 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 208 times.
✗ Branch 11 not taken.
208 std::copy(swap_table->indexes()->begin(), swap_table->indexes()->end(),
11130 std::back_inserter(swap_indexes));
11131
11132 /* Sort the index pointers according to the index names because the index
11133 ordanility of the partition being exchanged may be different than the
11134 table being swapped */
11135
1/2
✓ Branch 0 taken 208 times.
✗ Branch 1 not taken.
208 std::sort(part_indexes.begin(), part_indexes.end(),
11136 81 [](dd::Partition_index *a, dd::Partition_index *b) {
11137 81 return (a->name() < b->name());
11138 });
11139
1/2
✓ Branch 0 taken 208 times.
✗ Branch 1 not taken.
208 std::sort(swap_indexes.begin(), swap_indexes.end(),
11140 79 [](dd::Index *a, dd::Index *b) { return (a->name() < b->name()); });
11141
11142 /* Swap the se_private_data and options between indexes.
11143 The se_private_data should be swapped between every index of
11144 dd_part and swap_table; however, options should be swapped(checked)
11145 between part_table and swap_table */
11146
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 208 times.
208 ut_ad(part_indexes.size() == swap_indexes.size());
11147 208 for (p_iter = part_indexes.begin(), s_iter = swap_indexes.begin();
11148
5/6
✓ Branch 0 taken 249 times.
✓ Branch 1 taken 208 times.
✓ Branch 2 taken 249 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 249 times.
✓ Branch 5 taken 208 times.
457 p_iter < part_indexes.end() && s_iter < swap_indexes.end();
11149 249 p_iter++, s_iter++) {
11150 249 auto part_index = *p_iter;
11151 249 auto swap_index = *s_iter;
11152
1/2
✓ Branch 0 taken 249 times.
✗ Branch 1 not taken.
249 dd::Object_id p_tablespace_id = part_index->tablespace_id();
11153
2/4
✓ Branch 0 taken 249 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 249 times.
✗ Branch 3 not taken.
249 part_index->set_tablespace_id(swap_index->tablespace_id());
11154
1/2
✓ Branch 0 taken 249 times.
✗ Branch 1 not taken.
249 swap_index->set_tablespace_id(p_tablespace_id);
11155
11156
5/10
✓ Branch 0 taken 249 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 249 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 249 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 249 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✓ Branch 9 taken 249 times.
249 ut_ad(part_index->se_private_data().empty() ==
11157 swap_index->se_private_data().empty());
11158
5/10
✓ Branch 0 taken 249 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 249 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 249 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 249 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✓ Branch 9 taken 249 times.
249 ut_ad(part_index->se_private_data().size() ==
11159 swap_index->se_private_data().size());
11160
11161
3/6
✓ Branch 0 taken 249 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 249 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 249 times.
✗ Branch 5 not taken.
249 if (!part_index->se_private_data().empty()) {
11162 std::unique_ptr<dd::Properties> p_se_data(
11163
2/4
✓ Branch 0 taken 249 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 249 times.
✗ Branch 3 not taken.
249 dd::Properties::parse_properties(""));
11164
2/4
✓ Branch 0 taken 249 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 249 times.
✗ Branch 3 not taken.
249 p_se_data->insert_values(part_index->se_private_data());
11165
2/4
✓ Branch 0 taken 249 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 249 times.
✗ Branch 3 not taken.
249 part_index->se_private_data().clear();
11166
2/4
✓ Branch 0 taken 249 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 249 times.
✗ Branch 3 not taken.
249 part_index->set_se_private_data(swap_index->se_private_data());
11167
2/4
✓ Branch 0 taken 249 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 249 times.
✗ Branch 3 not taken.
249 swap_index->se_private_data().clear();
11168
1/2
✓ Branch 0 taken 249 times.
✗ Branch 1 not taken.
249 swap_index->set_se_private_data(*p_se_data);
11169 249 }
11170 }
11171 #ifdef UNIV_DEBUG
11172 208 for (s_iter = swap_indexes.begin(), pt_iter = part_table_indexes.begin();
11173
5/6
✓ Branch 0 taken 249 times.
✓ Branch 1 taken 208 times.
✓ Branch 2 taken 249 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 249 times.
✓ Branch 5 taken 208 times.
457 s_iter < swap_indexes.end() && pt_iter < part_table_indexes.end();
11174 249 pt_iter++, s_iter++) {
11175 249 auto part_table_index = *pt_iter;
11176 249 auto swap_index = *s_iter;
11177
11178
5/10
✓ Branch 0 taken 249 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 249 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 249 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 249 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✓ Branch 9 taken 249 times.
249 ut_ad(part_table_index->options().raw_string() ==
11179 swap_index->options().raw_string());
11180 }
11181 #endif
11182
11183 /* Swap the se_private_data and options of the two tables.
11184 Only the max autoinc should be set to both tables */
11185
2/2
✓ Branch 0 taken 37 times.
✓ Branch 1 taken 171 times.
208 if (m_part_share->get_table_share()->found_next_number_field) {
11186 37 uint64_t part_autoinc = part->autoinc;
11187 37 uint64_t swap_autoinc = swap->autoinc;
11188 37 uint64_t max_autoinc = std::max(part_autoinc, swap_autoinc);
11189
11190
2/4
✓ Branch 0 taken 37 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 37 times.
✗ Branch 3 not taken.
37 dd_set_autoinc(swap_table->se_private_data(), max_autoinc);
11191
1/2
✓ Branch 0 taken 37 times.
✗ Branch 1 not taken.
37 dd_set_autoinc(
11192
1/2
✓ Branch 0 taken 37 times.
✗ Branch 1 not taken.
37 part_table->se_private_data(),
11193 37 std::max<uint64>(swap_autoinc, m_part_share->next_auto_inc_val));
11194
11195
1/2
✓ Branch 0 taken 37 times.
✗ Branch 1 not taken.
37 dict_table_autoinc_lock(part);
11196
1/2
✓ Branch 0 taken 37 times.
✗ Branch 1 not taken.
37 dict_table_autoinc_initialize(part, max_autoinc);
11197
1/2
✓ Branch 0 taken 37 times.
✗ Branch 1 not taken.
37 dict_table_autoinc_unlock(part);
11198
11199
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 33 times.
37 if (m_part_share->next_auto_inc_val < swap_autoinc) {
11200
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 lock_auto_increment();
11201 4 m_part_share->next_auto_inc_val = swap_autoinc;
11202
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 unlock_auto_increment();
11203 }
11204 }
11205
11206 /* Swap the se_private_id between partition and table */
11207
11208
2/4
✓ Branch 0 taken 208 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 208 times.
✗ Branch 3 not taken.
208 dd_part->set_se_private_id(swap_table->se_private_id());
11209
1/2
✓ Branch 0 taken 208 times.
✗ Branch 1 not taken.
208 swap_table->set_se_private_id(p_se_id);
11210
11211
6/10
✓ Branch 0 taken 208 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 208 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 208 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 974 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 974 times.
✓ Branch 9 taken 208 times.
1182 for (auto dd_column : *swap_table->columns()) {
11212
3/6
✓ Branch 0 taken 974 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 974 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 974 times.
✗ Branch 5 not taken.
974 dd_column->se_private_data().set(dd_index_key_strings[DD_TABLE_ID],
11213 p_se_id);
11214 }
11215
11216
1/2
✓ Branch 0 taken 208 times.
✗ Branch 1 not taken.
208 dd_part_adjust_table_id(part_table);
11217
11218 208 func_exit:
11219 208 free(swap_name);
11220 208 free(part_name);
11221
11222 208 return error;
11223 214 }
11224